【问题标题】:How to collapse several string to one by key?如何按键将多个字符串折叠成一个?
【发布时间】:2017-11-30 05:50:03
【问题描述】:

例如我有一个文件:

key1   1212
key2   1212
key1   32332
key2   3232
key2   3232

我要获取文件:

key1   1212,32332
key2   1212,3232,3232

【问题讨论】:

  • 这在 bash 中应该是可能的,但我建议不要破坏你的大脑 - 使用更好的工具,如果你问 bash - 它是 linux - 肯定有 python - 使用它 - 任务变得简单。
  • 由于我的回答中的 cmets 而添加了 awk 和 python 标签。

标签: python bash perl awk


【解决方案1】:

它不是纯 sh/coreutils,但考虑使用 datamash 来完成此任务:

sed -r -e 's/[[:space:]]+/ /g' < infile.txt | datamash -t ' ' -s groupby 1 collapse 2

【讨论】:

    【解决方案2】:

    如果您想避免缓冲整个文件的结果(例如,如果文件非常大),您可以使用 sort 和 Python 的 itertools.groupby。像这样创建一个 Python 脚本:

    # group.py
    
    import itertools, sys
    
    for k, g in itertools.groupby(sys.stdin, lambda x: x.split()[0]):
        print(k, ",".join([x.split()[1] for x in g]))
    

    然后运行:

    sort file | python group.py 
    key1 1212,32332
    key2 1212,3232,3232
    

    否则,这个快速的 Perl 单行程序应该也可以通过在哈希中累积值来工作:

    perl -aE 'push @{$h{$F[0]}}, $F[1]; END {$"= ","; say "$_ @{$h{$_}}" for sort keys %h}' file
    

    输出:

    key1 1212,32332
    key2 1212,3232,3232
    

    【讨论】:

      【解决方案3】:

      在 awk 中:

      $ awk '{a[$1]=a[$1](a[$1]==""?"":",")$2}END{for(i in a)print i,a[i]}' file
      key1 1212,32332
      key2 1212,3232,3232
      

      解释:

      awk '{                                        # use awk for this kind of stuff
          a[$1]=a[$1] ( a[$1]=="" ? "" : "," ) $2   # hash on first col and append seconds
      }
      END {                                         # after everything is hashed
          for(i in a)                               # for each entry in hash a
              print i,a[i]                          # output key and data
      }' file                                       # oh yeah the file
      

      编辑:我们可以使用sort 对文件进行排序,然后在逗号之后输出键和所有数据,而不是让 awk 进行缓冲(即散列到 a)分开。后半部分再次使用 awk:

      $ sort file | awk '$1!=p{printf "%s%s",(NR>1?ORS:""),$1}{printf "%s%s", ($1==p?",":OFS),$2;p=$1}END{print ""}'
      key1 1212,32332
      key2 1212,3232,3232
      

      这里sort 没有给出任何花哨的参数,但在现实世界中可能需要一些参数。 awk部分解释:

      sort file | \                          # sort the file
      awk '                                  # before feeding to awk
      $1!=p {                                # if key is different from previous key
          printf "%s%s",(NR>1?ORS:""),$1     # newline and print the key
      }
      {
          printf "%s%s", ($1==p?",":OFS),$2  # print the data comma-separated 
          p=$1                               # store key for comparing on the next round
      }
      END{ 
          print ""                           # finish the last line nicely
      }'
      

      【讨论】:

      • 答案很可能是好的并且有效,但我总是试图让人们远离使用 bash 解析文本文件 - 它效率低下。而且您不知道该输入文件有多大,因此它可能非常糟糕:)和Bdfy,考虑到您的SO声誉-如果您将标签python添加到您的问题中,我将提供可行的python解决方案,我认为比 bash 好用。
      • @Drako :话虽如此,awk 非常适合小文件
      • @sjsam 我同意,但如果输入是 500MB 或更多,你会真的对 python 解决方案更满意 :) 这就是为什么即使是小的我也倾向于使用正确的工具,因为你永远不知道当它可能增长:)
      • @Drako 添加了另一个依赖于排序输入的 awk 版本。顺便说一句,我看不出 awk 会比 Python 解决方案消耗更多的资源。我将在问题中添加 awk 和 python 标签。
      【解决方案4】:
      awk '{a[$1]=(a[$1]!="")?a[$1]","$2:$2}END{for(i in a){print i "\t" a[i]}}' file
      key1    1212,32332
      key2    1212,3232,3232
      

      应该这样做。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-08
        • 1970-01-01
        • 1970-01-01
        • 2015-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-26
        相关资源
        最近更新 更多