Ruby:如何在数组中查找非唯一元素并打印每个元素的出现次数?

21

I have

a = ["a", "d", "c", "b", "b", "c", "c"]

需要打印类似以下内容(按出现次数降序排列):
c:3
b:2

我理解第一部分(查找非唯一项)是:

b = a.select{ |e| a.count(e) > 1 }
=> ["c", "b", "b", "c", "c"] 

或者

puts b.select{|e, c| [e, a.count(e)] }.uniq

c
b

如何按出现次数反向排序并输出每个非唯一值?
8个回答

22
puts a.uniq.
       map { | e | [a.count(e), e] }.
       select { | c, _ | c > 1 }.
       sort.reverse.
       map { | c, e | "#{e}:#{c}" }

非常优雅!'_'代表什么?我以前没见过。 - Richard Brown
3
一个通常表示未使用的变量名。 - Linuxios
2
你也可以在未使用的变量名前使用下划线,例如 _temporary。它具有相同的目的,但更加描述性。 - Ryan Clark
1
这不是同一个目的。在 Ruby 中,_ 是一个特殊的变量名(从 Perl 继承而来),许多方法默认情况下都会写入该变量。 _temporary 或任何以 _... 开头的变量并不属于该类别,它是一个独立的、单独存在于普通变量列表中的变量。 - the Tin Man
3
我知道“_”也有特殊用途,但在这种情况下,它被用作已赋值但未使用的变量,并且为了抑制Ruby在此处生成的警告,我认为这两个选项的作用相同。 - Ryan Clark

8
group_by方法经常被用来实现这个功能:
a.group_by{ |i| i }
{
    "a" => [
        [0] "a"
    ],
    "d" => [
        [0] "d"
    ],
    "c" => [
        [0] "c",
        [1] "c",
        [2] "c"
    ],
    "b" => [
        [0] "b",
        [1] "b"
    ]
}
我喜欢这样做:
a.group_by{ |i| i }.each_with_object({}) { |(k,v), h| h[k] = v.size }
{
    "a" => 1,
    "d" => 1,
    "c" => 3,
    "b" => 2
}
或者:
Hash[a.group_by{ |i| i }.map{ |k,v| [k, v.size] }]
{
    "a" => 1,
    "d" => 1,
    "c" => 3,
    "b" => 2
}
其中一个可能会解决你的问题。从那里,你可以使用一个小测试来缩小结果:
Hash[a.group_by{ |i| i }.map{ |k,v| v.size > 1 && [k, v.size] }]
{
    "c" => 3,
    "b" => 2
}
如果你只想打印信息,请使用:
puts a.group_by{ |i| i }.map{ |k,v| "#{k}: #{v.size}" }
a: 1
d: 1
c: 3
b: 2

1
group_by{|i| i} can now be expressed as group_by(&:itself) - Rich

3

从 Ruby 2.7 开始,你可以利用 Enumerable#tally 和带编号的块参数:

a = ["a", "d", "c", "b", "b", "c", "c"]
puts a.tally.filter { _2 > 1 }.sort_by { -_2 }.map &:first

在这里,Enumerable#tally返回一个像{ 'a' => 1, 'b' => 2, ... }的哈希表,然后您需要过滤和排序。排序后,哈希表将被折叠为嵌套数组,例如[['b', 2], ...]。最后一步是使用&:first取每个数组元素的第一个参数。

1
怎么样:
a.sort.chunk{|x| a.count(x)}.sort.reverse.each do |n, v|
  puts "#{v[0]}:#{n}" if n > 1
end

1

我个人喜欢这个解决方案:

 a.inject({}) {|hash, val| hash[val] ||= 0; hash[val] += 1; hash}.
   reject{|key, value| value == 1}.sort.reverse.
   each_pair{|k,v| puts("#{k}:#{v}")}

0

这将为您提供一个带有元素 => 出现次数的哈希表:

b.reduce(Hash.new(0)) do |hash, element|
  hash[element] += 1
  hash
end

1
这个代码块可以更加优雅地书写:hash.update(element => hash[element] + 1) - undur_gongor

0
puts a.uniq.
     map { |e| a.count(e) > 1 ? [e, a.count(e)] : nil }.compact.
     sort { |a, b| b.last <=> a.last }

2
@undur_gongor 我不认为它是,但这与只有 a.uniq 是一样的... 无论如何,我在我的代码中也保留了唯一值,这使得它对于问题来说不好,并且我已经修复了它。 - oldergod

0
a.reduce(Hash.new(0)) { |memo,x| memo[x] += 1; memo } # Frequency count.
  .select { |_,count| count > 1 } # Choose non-unique items.
  .sort_by { |x| -x[1] } # Sort by number of occurrences descending.
# => [["c", 3], ["b", 2]]

另外:

a.group_by{|x|x}.map{|k,v|[k,v.size]}.select{|x|x[1]>1}.sort_by{|x|-x[1]}
# => [["c", 3], ["b", 2]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接