【问题标题】:Most frequent element in an array (bash 3.2)数组中最常见的元素(bash 3.2)
【发布时间】:2017-04-16 18:10:27
【问题描述】:

在使用 bash 的 shell 脚本中,我想找到一个数组中出现频率最高的数字,并将结果存储在变量 $result 中。该数组可以有任意数量的值。如果返回多个结果,那么我想选择最小的数字。

我了解 bash 可能不是解决此问题的最佳工具,我愿意接受有关使用 Mac OS X 系统上脚本中命令行中可用工具的建议。

例子:

array=(03 03 03 04 04 04 04)
3 次出现 03
4 次出现 04
应该将 04 返回到名为 $result 的变量中。

另一个例子:

数组=(03 03 03 03 04 04 04 04)
4 次出现 03
4 次出现 04
选择最小的数字,即 03
应该将 03 返回到名为 $result 的变量中。

感谢您的帮助。

【问题讨论】:

  • 我不确定它是否有助于解决这个特定问题。我不是 bash 大师或任何东西。但无论如何,您都可以通过Homebrew 轻松获得更新版本的 bash。我相信的最新版本是 4.4。较新版本中提供了许多其他有用的功能。

标签: arrays bash macos shell


【解决方案1】:

您的问题有一个歧义需要解决:您说数组是 numbers 的数组,但该示例为它们提供了前导零,如果您这样做会导致一些意外将字符串视为数字(它们将被解释为八进制)。

除此之外,解决方案比较简单:使用sortuniq统计每个值的实例数,将结果按count排序,然后提取第一个值。为了满足排序的要求,我们首先使用printf将数组每行一个元素写入:

printf '%s\n' "${arr[@]}" | sort | uniq -c |
sort -k1,1nr -k2 | awk '{print $2; exit}'

sort 的两次调用都将原始数据排序为字符串。如果您真的想将它们排序为数字,您可以使用:

printf '%d\n' "${arr[@]}" | sort -n | uniq -c |
sort -k1,1nr -k2n | awk '{print $2; exit}'

虽然这会将所有数字标准化为规范形式(这样03 将变为3)。

【讨论】:

  • 这不适用于 --> arr=(03 03 03 04 04 04 04)
  • @keelDev:这个答案中有(嗯,曾经)三个建议;您没有指定哪个不起作用,但经过反思,它必须是最后一个,因为 printf 是错误的(更严重的是,使用 uniq)。所以我删除了它;其他两个在各自的约束范围内工作正常(将数据视为字符串或规范化数字)。
【解决方案2】:

这是一个基于 awk 的解决方案,它避免了 bash 关联数组:

#!/bin/bash
get_result(){
awk '
  { 
      n=++hsh[$1]
      if(n>max_occ){
         max_occ=n
         what=$1
      }else if(n==max_occ){
         if(what>$1) 
             what=$1
      }
  } 
  END { print what }
'
}

array=(03 03 03 04 04 04 04)
result=$(printf "%s\n" "${array[@]}" |  get_result)
echo $result

array=(03 03 03 03 04 04 04 04)
result=$(printf "%s\n" "${array[@]}" |  get_result)
echo $result

结果是 03 和 04,如您的示例所示。

【讨论】:

    【解决方案3】:

    您可以使用关联数组来跟踪数组元素的频率:

    #!/bin/bash
    
    arr=(1 2 3 4 4 44 4 4 5 5)
    declare -A hash
    max_times=0
    for i in "${arr[@]}"; do
      ((hash[$i]++))
      h=${hash[$i]}
      if [[ $h > $max_times ]]; then
        max=$i
        max_times=$h
      fi
    done
    
    echo max=$max, max_times=$max_times
    

    输出:

    max=4, max_times=4
    

    如果我们不能使用关联数组,那么我们可以使用外部工具:

    array=(1 3 3 333 5 66 5 33 66 66 33 22 11)
    printf '%d\n' "${array[@]}" | sort -n | uniq -c | sort -n | tail -1
    

    输出:

      3 66
    

    【讨论】:

    • 感谢您的回复,不幸的是我只有 bash 3.2 可用,并且不希望在 Mac OS X 上升级 bash。据我所知,声明功能在 bash 版本
    • @KeelDev declare 本身早于 Bash 4.0,但关联数组的 declare -A 是 4.0 或更高版本。
    猜你喜欢
    • 2015-02-21
    • 1970-01-01
    • 2019-05-25
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多