【问题标题】:Removing duplicates on a variable without sorting在不排序的情况下删除变量上的重复项
【发布时间】:2009-12-09 09:38:59
【问题描述】:

我有一个变量,其中包含以下空格分隔的条目。

variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"

如何在不排序的情况下删除重复项?

#Something like this.
new_variable="apple lemon papaya avocado grapes mango banana"

我在某处找到了一个脚本,该脚本可以完成删除变量的重复项,但会对内容进行排序。

#Not something like this.
new_variable=$(echo "$variable"|tr " " "\n"|sort|uniq|tr "\n" " ")
echo $new_variable
apple avocado banana grapes lemon mango papaya

【问题讨论】:

    标签: bash unix shell sorting variables


    【解决方案1】:
    new_variable=$( awk 'BEGIN{RS=ORS=" "}!a[$0]++' <<<$variable );
    

    它是这样工作的:

    RS(输入记录分隔符)设置为空白,以便将 $variable 中的每个水果视为记录而不是字段。 !a[$0]++ 发生了非排序的独特魔法。由于 awk 支持关联数组,它使用当前记录 ($0) 作为数组 a[] 的键。如果之前没有看到该键,则 a[$0] 的计算结果为 '0'(awk 未设置索引的默认值),然后将其取反以返回 TRUE。然后我利用了这样一个事实,即如果表达式返回 TRUE 并且没有给出“{ commands }”,则 awk 将默认为“print $0”。最后,a[$0] 然后递增,这样这个键就不能再返回 TRUE,因此永远不会打印重复值。 ORS(输出记录分隔符)也设置为空格以模仿输入格式。

    产生相同输出的这个命令的一个不太简洁的版本如下:

    awk 'BEGIN{RS=ORS=" "}{ if (a[$0] == 0){ a[$0] += 1; print $0}}'
    

    一定要爱 awk =)

    编辑

    如果您需要在纯 Bash 2.1+ 中执行此操作,我建议您这样做:

    #!/bin/bash    
    
    variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
    temp="$variable"
    
    new_variable="${temp%% *}"
    
    while [[ "$temp" != ${new_variable##* } ]]; do
       temp=${temp//${temp%% *} /}
       new_variable="$new_variable ${temp%% *}"
    done
    
    echo $new_variable;
    

    【讨论】:

    • 甜蜜 :) 感谢您的解释。
    • 简单地测试成员比计算好: awk 'BEGIN{RS=ORS=" "} { if (!($0 in a)) { a[$0]; print } }' 或者更简洁: awk 'BEGIN{RS=ORS=" "} !($0 in a || a[$0])'
    • @Mark:在 10,000 次迭代的循环中做一个“时间”表明你的速度慢了 3% 以上。不是很多,但也不是更好。这种差异只会随着元素数量的增加而变得更大,因为您的版本需要 O(n) 时间,而我的版本始终是常数 O(1)。
    • 非常好的解决方案,谢谢。除非最后发现重复,否则它一个接一个地不起作用。例如:variable="apple lemon papaya papaya" 打印:apple lemon papaya papaya。而如果我有: variable="apple lemon papaya papaya mango" 那么它会删除重复的 papaya 并打印: apple lemon papaya mango。想法?
    • 找到以下解决方案,帮助解决我之前评论中概述的问题:stackoverflow.com/questions/46185241/… 感谢您分享您的解决方案。
    【解决方案2】:

    此管道版本通过保留原始顺序来工作:

    variable=$(echo "$variable" | tr ' ' '\n' | nl | sort -u -k2 | sort -n | cut -f2-)
    

    【讨论】:

    • 这是唯一适合我的解决方案。 awk 解决方案仍然有重复项。谢谢。
    【解决方案3】:

    纯猛击:

    variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
    
    declare new_value=''
    
    for item in $variable; do
      if [[ ! $new_value =~ $item ]] ; then   # first time?
        new_value="$new_value $item"
      fi
    done
    new_value=${new_value:1}                  # remove leading blank
    

    【讨论】:

    • 很好的解决方案,但请注意,由于 '=~' 运算符,这会将您锁定在 Bash 3.X 中。
    【解决方案4】:

    纯便携的sh

    words="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana" seen= for word in $words; do case $seen in $word\ * | *\ $word | *\ $word\ * | $word) # already seen ;; *) seen="$seen $word" ;; esac done echo $seen

    【讨论】:

      【解决方案5】:

      外壳

      declare -a arr
      variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
      set -- $variable
      count=0
      for c in $@
      do
          flag=0
          for((i=0;i<=${#arr[@]}-1;i++))
          do
              if [ "${arr[$i]}" == "$c" ] ;then
                  flag=1
                  break
              fi
          done
          if  [ "$flag" -eq 0 ] ; then
              arr[$count]="$c"
              count=$((count+1))
          fi
      done
      for((i=0;i<=${#arr[@]}-1;i++))
      do
         echo "result: ${arr[$i]}"
      done
      

      运行结果:

      linux# ./myscript.sh
      result: apple
      result: lemon
      result: papaya
      result: avocado
      result: grapes
      result: mango
      result: banana
      

      或者如果你想使用 gawk

      awk 'BEGIN{RS=ORS=" "} (!($0 in a) ){a[$0];print}'
      

      【讨论】:

        【解决方案6】:

        Z 壳:

        % variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
        % print ${(zu)variable}                                                               
        apple lemon papaya avocado grapes mango banana
        

        【讨论】:

          【解决方案7】:

          另一个awk解决方案:

          #!/bin/bash
          variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
          variable=$(printf '%s\n' "$variable" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}')
          variable="${variable%,*}"
          echo "$variable"
          

          输出:

          apple lemon papaya avocado grapes mango banana
          

          【讨论】:

            【解决方案8】:

            Perl 解决方案:

            perl -le 'for (@ARGV){ $h{$_}++ }; for (keys %h){ print $_ }' $variable

            @ARGV是来自$variable的输入参数列表
            循环遍历列表,使用循环变量 $_ 填充 h 散列
            循环遍历 h 哈希的键,并打印每个键

            grapes
            avocado
            apple
            lemon
            banana
            mango
            papaya
            

            这个变体打印输出首先按频率排序$h{$a} &lt;=&gt; $h{$b} 然后按字母顺序$a cmp $b

            perl -le 'for (@ARGV){ $h{$_}++ }; for (sort { $h{$a} &lt;=&gt; $h{$b} || $a cmp $b } keys %h){ print "$h{$_}\t$_" }' $variable

            1       banana
            1       grapes
            1       mango
            2       apple
            2       avocado
            2       lemon
            2       papaya
            

            此变体产生与上一个变体相同的输出。
            但是,不是输入 shell 变量,而是使用输入文件“fruits”,每行一个水果:

            perl -lne '$h{$_}++; END{ for (sort { $h{$a} &lt;=&gt; $h{$b} || $a cmp $b } keys %h){ print "$h{$_}\t$_" } }' fruits

            【讨论】:

              猜你喜欢
              • 2018-12-24
              • 1970-01-01
              • 1970-01-01
              • 2020-08-28
              • 2015-06-14
              • 2014-04-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多