【问题标题】:Remove duplicate from csv using bash / awk使用 bash / awk 从 csv 中删除重复项
【发布时间】:2018-03-24 11:43:49
【问题描述】:

我有一个 csv 文件,格式为:

"id-1"|"A"
"id-2"|"C"
"id-1"|"B"
"id-1"|"D"
"id-2"|"B"
"id-3"|"A"
"id-3"|"A"
"id-1"|"B"

我想在单行中按第一列唯一 ID 和连续类型进行分组,如下所示:

"id-1"|"A:B:D"
"id-2"|"B:C"
"id-3"|"A"

我发现 awk 在处理此类情况方面做得很好。但我能做到的就是:

"id-1"|"A":"B":"D":"B"
"id-2"|"B":"C"
"id-3"|"A":"A"

我使用了这个命令:

awk -F "|" '{if(a[$1])a[$1]=a[$1]":"$2; else a[$1]=$2;}END{for (i in a)print i, a[i];}' OFS="|" file

如何删除重复项并处理第二列类型的格式?

【问题讨论】:

  • 为什么C 值在前时输出中的"id-2"|"B:C" 而不是"id-2"|"C:B"
  • @anubhava 我也在寻找排序结果列表。
  • @Qedrix 请注意,任何使用 in 运算符(例如 for (i in array))的 awk 解决方案,除非它是 gawk 并设置 sorted_in 不会产生排序输出 - 如果输出看起来像它排序后,这与您的特定数据集纯属巧合,您可以确定它不会与其他输入。

标签: bash csv awk duplicates


【解决方案1】:

快速修复:

$ awk -F "|" '!seen[$0]++{if(a[$1])a[$1]=a[$1]":"$2; else a[$1]=$2;}END{for (i in a)print i, a[i];}' OFS="|" file 
"id-1"|"A":"B":"D"
"id-2"|"C":"B"
"id-3"|"A"
  • !seen[$0]++ 仅在尚未看到线时才为真


如果第二列都应该在双引号内

$ awk -v dq='"' 'BEGIN{FS=OFS="|"}
                 !seen[$0]++{a[$1]=a[$1] ? a[$1]":"$2 : $2}
                 END{for (i in a){gsub(dq,"",a[i]); print i, dq a[i] dq}}' file
"id-1"|"A:B:D"
"id-2"|"C:B"
"id-3"|"A"

【讨论】:

  • 第二行不应该是"B:C"吗?
  • @RomanPerekhrest 我没有对它进行排序,希望 OP 会澄清这是否是一个要求
【解决方案2】:

使用 GNU awk 实现真正的多维数组以及 gensub() 和 sorted_in:

$ awk -F'|' '
    { a[$1][gensub(/"/,"","g",$2)] }
    END {
        PROCINFO["sorted_in"] = "@ind_str_asc"
        for (i in a) {
            c = 0
            for (j in a[i]) {
                printf "%s%s", (c++ ? ":" : i "|\""), j
            }
            print "\""
        }
    }
' file
"id-1"|"A:B:D"
"id-2"|"B:C"
"id-3"|"A"

输出的行和列都将按升序按字符串排序(即按字符的字母顺序)。

【讨论】:

  • 这是最好的答案。虽然我不明白它是如何得到B:C 而不是C:B,因为C 首先出现在输入中。
  • 纯属巧合。 in 运算符以哈希顺序(或其他随机性,但这是通常的...)访问数组索引,因此输出可以是任何顺序。
  • @anubhava OP 刚刚添加了一条评论,她希望对输出进行排序,所以我添加了sorted_in 语句来处理这个问题。
【解决方案3】:

短 GNU datamash + tr 解决方案:

datamash -st'|' -g1 unique 2 <file | tr ',' ':'

输出:

"id-1"|"A":"B":"D"
"id-2"|"B":"C"
"id-3"|"A"

---------

如果应该删除 between-item 双引号 - 使用以下替代方案:

datamash -st'|' -g1 unique 2 <file | sed 's/","/:/g'

输出:

"id-1"|"A:B:D"
"id-2"|"B:C"
"id-3"|"A"

【讨论】:

    【解决方案4】:

    对于示例,输入低于 1 将起作用,但未排序

    单线

    # using two array ( recommended )
    awk 'BEGIN{FS=OFS="|"}!seen[$1,$2]++{a[$1] = ($1 in a ? a[$1] ":" : "") $2}END{for(i in a)print i,a[i]}' infile
    
    # using regexp 
    awk 'BEGIN{FS=OFS="|"}{ a[$1] = $1 in a ? ( a[$1] ~ ("(^|:)"$2"(:|$)") ? a[$1] :  a[$1]":"$2  ) : $2}END{for(i in a)print i,a[i]}' infile
    

    测试结果:

    $ cat infile
    "id-1"|"A"
    "id-2"|"C"
    "id-1"|"B"
    "id-1"|"D"
    "id-2"|"B"
    "id-3"|"A"
    "id-3"|"A"
    "id-1"|"B"
    
    $ awk 'BEGIN{FS=OFS="|"}!seen[$1,$2]++{a[$1] = ($1 in a ? a[$1] ":" : "") $2}END{for(i in a)print i,a[i]}' infile
    "id-1"|"A":"B":"D"
    "id-2"|"C":"B"
    "id-3"|"A"    
    
    $ awk 'BEGIN{FS=OFS="|"}{ a[$1] = $1 in a ? ( a[$1] ~ ("(^|:)"$2"(:|$)") ? a[$1] :  a[$1]":"$2  ) : $2}END{for(i in a)print i,a[i]}' infile
    "id-1"|"A":"B":"D"
    "id-2"|"C":"B"
    "id-3"|"A"
    

    更好的可读性:

    使用正则表达式

    awk 'BEGIN{
               FS=OFS="|"
         }
         { 
               a[$1] =$1 in a ?(a[$1] ~ ("(^|:)"$2"(:|$)") ? a[$1] : a[$1]":"$2):$2
         }
         END{
               for(i in a)
                  print i,a[i]
         }
         ' infile
    

    使用两个数组

    awk 'BEGIN{
              FS=OFS="|"
         }
         !seen[$1,$2]++{ 
                 a[$1] = ($1 in a ? a[$1] ":" : "") $2
         }
      END{
               for(i in a)
                   print i,a[i]
         }' infile
    

    注意:您也可以使用!seen[$0]++,它将使用整行作为索引,但如果是您的真实数据,如果 你想更喜欢一些其他的专栏,你可能更喜欢!seen[$1,$2]++, 这里 column1 和 column2 用作索引

    【讨论】:

      【解决方案5】:

      awk + sort 解决方案:

      awk -F'|' '{ gsub(/"/,"",$2); a[$1]=b[$1]++? a[$1]":"$2:$2 }
                 END{ for(i in a) printf "%s|\"%s\"\n",i,a[i] }' <(sort -u file)
      

      输出:

      "id-1"|"A:B:D"
      "id-2"|"B:C"
      "id-3"|"A"
      

      【讨论】:

        猜你喜欢
        • 2020-05-18
        • 1970-01-01
        • 2021-08-20
        • 1970-01-01
        • 1970-01-01
        • 2014-10-13
        • 2019-08-04
        • 2014-10-16
        • 2016-03-29
        相关资源
        最近更新 更多