Reputation: 1547
I have
a = ["a", "d", "c", "b", "b", "c", "c"]
and need to print something like (sorted descending by number of occurrences):
c:3
b:2
I understand first part (finding NON-unique) is:
b = a.select{ |e| a.count(e) > 1 }
=> ["c", "b", "b", "c", "c"]
or
puts b.select{|e, c| [e, a.count(e)] }.uniq
c
b
How to output each non-unique with number of occurrences sorted backwards?
Upvotes: 21
Views: 12939
Reputation: 173532
From Ruby 2.7, you can utilise Enumerable#tally
and numbered block arguments:
a = ["a", "d", "c", "b", "b", "c", "c"]
puts a.tally.filter { _2 > 1 }.sort_by { -_2 }.map &:first
Here, Enumerable#tally
returns a hash like { 'a' => 1, 'b' => 2, ... }
, which you then have to filter and sort. After sorting, the hash would've collapsed to a nested array, e.g. [['b', 2], ...]
. The last step is to take the first argument of each array element, using &:first
.
Upvotes: 3
Reputation: 2230
I personally like this solution:
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}")}
Upvotes: 1
Reputation: 15954
puts a.uniq.
map { | e | [a.count(e), e] }.
select { | c, _ | c > 1 }.
sort.reverse.
map { | c, e | "#{e}:#{c}" }
Upvotes: 22
Reputation: 15010
puts a.uniq.
map { |e| a.count(e) > 1 ? [e, a.count(e)] : nil }.compact.
sort { |a, b| b.last <=> a.last }
Upvotes: 0
Reputation: 54984
How about:
a.sort.chunk{|x| a.count(x)}.sort.reverse.each do |n, v|
puts "#{v[0]}:#{n}" if n > 1
end
Upvotes: 1
Reputation: 874
This will give you a hash with element => occurrences
:
b.reduce(Hash.new(0)) do |hash, element|
hash[element] += 1
hash
end
Upvotes: 0
Reputation: 160551
The group_by
method is used for this often:
a.group_by{ |i| i } { "a" => [ [0] "a" ], "d" => [ [0] "d" ], "c" => [ [0] "c", [1] "c", [2] "c" ], "b" => [ [0] "b", [1] "b" ] }
I like:
a.group_by{ |i| i }.each_with_object({}) { |(k,v), h| h[k] = v.size } { "a" => 1, "d" => 1, "c" => 3, "b" => 2 }
Or:
Hash[a.group_by{ |i| i }.map{ |k,v| [k, v.size] }] { "a" => 1, "d" => 1, "c" => 3, "b" => 2 }
One of those might scratch your itch. From there you can reduce the result using a little test:
Hash[a.group_by{ |i| i }.map{ |k,v| v.size > 1 && [k, v.size] }] { "c" => 3, "b" => 2 }
If you just want to print the information use:
puts a.group_by{ |i| i }.map{ |k,v| "#{k}: #{v.size}" } a: 1 d: 1 c: 3 b: 2
Upvotes: 8
Reputation: 156384
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]]
Also:
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]]
Upvotes: 0