根据输入字符串计算部门的平均工资

根据输入字符串计算部门的平均工资

如果用户以字符串形式输入员工详细信息,并且我们希望输出显示部门的平均工资,并且输出的顺序应与输入字符串中出现的顺序相同。

例如输入字符串:

EMP101:Jack:HR:6000#EMP102:Jill:Management:10000#EMP103:Russell:Testing:10000#EMP104:Monica:HR:15000#EMP105:John:Management:25000#EMP106:Ram:Testing:8000#EMP107:Tan:HR:15000#EMP108:Harry:Management:10000

输出字符串:

HR:12000#Management:15000#Testing:9000

我尝试了下面的代码:

echo "EMP101:Jack:HR:6000#EMP102:Jill:Management:10000#EMP103:Russell:Testing:10000#EMP104:Monica:HR:15000#EMP105:John:Management:25000#EMP106:Ram:Testing:8000#EMP107:Tan:HR:15000#EMP108:Harry:Management:10000" \
| awk 'BEGIN{RS="#"; OFS=FS=":"} {gsub(/"\n$/,"",$5); print $3,$4}' \
| awk -F ":" '{a[$1] += $2} {b[$1] += 1} END{for (i in a) print i, a[i]/b[i]}' \
| tr " " ":" \
| tr "\n" "#";

得到的输出为:

:0#Testing:9000#Management:15000#HR:12000

这不是我想要的,而且我也不明白为什么:0#要在字符串的开头添加。谁能告诉我如何在 shell 脚本中实现这一点

答案1

使用任何awk并保留顺序:

awk 'BEGIN{ RS="#"; FS=OFS=":" }
   { ($3 in dept)?"":ordrDept[++i]=$3; dept[$3]+=$4; seenDept[$3]++; }
END{ for(o=1; o<=i; o++)
         printf "%s%d%s", ordrDept[o] OFS, 
                          dept[ordrDept[o]]/seenDept[ordrDept[o]], 
                          (i==o)?ORS:RS
}' infile

输出:

HR:12000#Management:15000#Testing:9000

我们使用了ordrDept数组按照他们见过的顺序记住每个部门的顺序。
我们使用了dept 数组汇总每个部门的总工资。
我们使用了seenDept数组来记住他们被见过多少次。

END{...}块中,i是我们访问过的现有部门的最大数量ordrDept[++i]=$3,然后我们按照我们首先访问的顺序打印它们ordrDept[o],然后是每个部门的平均工资计算total/count,并且(i==o)?ORS:RS我们控制分隔符。


上述方法的另一种实现方法是使用更有意义的变量名(感谢@EdMorton):

BEGIN {
    RS = "#"
    FS = OFS = ":"
}
!($3 in depts2sals) {
    depts[++numDepts] = $3
}
{
    depts2sals[$3] += $4
    depts2cnts[$3]++
}
END {
    for (deptNr=1; deptNr<=numDepts; deptNr++) {
        dept = depts[deptNr]
        sal = depts2sals[dept]
        cnt = depts2cnts[dept]
        avg = sal / cnt
        printf "%s%s%s", dept OFS, avg, (deptNr<numDepts ? RS : ORS)
    }
}

答案2

主题的另一种变化

echo 'EMP101:Jack:HR:6000#EMP102:Jill:Management:10000#EMP103:Russell:Testing:10000#EMP104:Monica:HR:15000#EMP105:John:Management:25000#EMP106:Ram:Testing:8000#EMP107:Tan:HR:15000#EMP108:Harry:Management:10000' |
    awk -F'#' '
        # Split into lines at "#"
        {
            for(f=1; f<=NF; f++) {
                print $f;
            }
        }
    ' |
    awk -F: '
        # Record department if unknown. Keep running total and count
        {
            if (!(sum[$3]+0)) { pos[idx++]=$3 };
            sum[$3]+=$4;
            count[$3]++;
        }
        # Output department in order of recording, and calculate average
        END {
            for (idx in pos) {
                dept=pos[idx];
                printf "#%s:%d", dept, sum[dept]/count[dept];
            };
            printf "\n";
        }
    ' |
    cut -c2-

输出

HR:12000#Management:15000#Testing:9000

答案3

:0#是由于输入/文件中存在附加的尾随空行。您尝试删除该内容失败,因为您提供了错误的模式gsub(...)以及不存在的字段。更正它,或者使用扩展RS变量。因为awk它本身就相当强大,不需要运行那堆管道;只做一件awk

尝试

awk 'BEGIN{ORS="#"; RS="[#\n]"; OFS=FS=":"} {SUM[$3]+=$4;CNT[$3]++} END{for (s in SUM) print s, SUM[s]/CNT[s]} ' file3
Management:15000#Testing:9000#HR:12000#

删除尾随的 ORS 留给读者作为练习。

答案4

这是一种使用方法珀尔

echo 'your-string' |
perl -lnse 'while ( /:([^:#]+):(\d+)(?=#|$)/g )
{
  my @A = ($2,1);
  exists $s{$1} or $D[@D]=$1;
  $_->{$1} += shift(@A) for \(%s,%k);
}
print map { join(":", $_, $s{$_}/$k{$_}) } @D;
' -- -,=\#

涉及到的数据结构:

  • 以部门为键的哈希 %s 保存来自同一部门的员工的相应工资总额。
  • 以部门为键的哈希 %k 保存该部门的员工数量。
  • 数组 @D 按照遇到的顺序包含部门,不重复。

以下是从您的尝试中获取的方法

echo 'your_string' |
tr '#' '\n' |
awk -F: '{print $3, NR, $4}' |
sort -k1 -k2n |
awk '
prev != $1 {
  if (NR > 1) print prev, sum/knt
  knt=sum=0;prev=$1
}
{ knt++; sum += $3 }
END { ORS=RS; print prev, sum/knt }
' OFS=: ORS=\#

给出了 GNU sed 方法。这个想法是将同一部门收集在一起,并在同一部门内将工资分开。

## dc code to compute average of a list of numbers
avg='[+z1<a]sa\3zsnlaxln/f'

echo 'your_string' |
sed -Ee '
  y/#/\n/
  s/^([^:]*:){2}//Mg;ta
  :a
  s/((^|\n)([^:]+):([0-9]+( [0-9]+)?))(\n(.*\n)?)\3:([0-9]+)(\n|$)/\1 \8\6/
  ta
  :b'"
  s#^((.*\n)?[^:]+:)([0-9]+( [0-9]+)+)((\n.*)?)#printf %s '\\1' \"\$(echo '$avg'|dc)\" '\\5'#e
  tb
  y/\n/#/
"

相关内容