I have an array of hashes like this:
[
{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"second", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"third", "b"=>[{"lt"=>9, "lg"=>10}]}
]
How can I display occurrences by "a"
key value and display something like this
[
{{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]}=>2},
{{"a"=>"second", "b"=>[{"lt"=>7, "lg"=>8}]}=>1},
{{"a"=>"third", "b"=>[{"lt"=>9, "lg"=>10}]}=>1}
]
I am looking for "ruby way" solution.
You could define Enumerable#frequency_distribution
by using each_with_object
with a Hash and a default occurence value of 0
:
module Enumerable
def frequency_distribution
each_with_object(Hash.new(0)) { |element, count| count[element] += 1 }
end
end
It works this way :
require 'pp'
data = [
{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"second", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"third", "b"=>[{"lt"=>9, "lg"=>10}]}
]
pp data.frequency_distribution
# {{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]}=>2,
# {"a"=>"second", "b"=>[{"lt"=>7, "lg"=>8}]}=>1,
# {"a"=>"third", "b"=>[{"lt"=>9, "lg"=>10}]}=>1}
If you don't want to monkey-patch Enumerable
:
def frequency_distribution(array)
array.each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
end
frequency_distribution(data)
# {{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]}=>2,
# {"a"=>"second", "b"=>[{"lt"=>7, "lg"=>8}]}=>1,
# {"a"=>"third", "b"=>[{"lt"=>9, "lg"=>10}]}=>1}
Note that the output is one single hash. Keys are hashes and values are integers. I cannot think of any good reason to convert it to an array of 1-pair hashes. Lookup would be much slower and less readable.
For a more generic method, you could define Enumerable#count_by
, with the same syntax as group_by
or sort_by
:
module Enumerable
def count_by(&block)
each_with_object(Hash.new(0)) { |element, count| count[block.call(element)] += 1 }
end
end
require 'pp'
data = [
{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"second", "b"=>[{"lt"=>7, "lg"=>8}]},
{"a"=>"third", "b"=>[{"lt"=>9, "lg"=>10}]}
]
pp data.count_by(&:itself)
# {{"a"=>"first", "b"=>[{"lt"=>7, "lg"=>8}]}=>2,
# {"a"=>"second", "b"=>[{"lt"=>7, "lg"=>8}]}=>1,
# {"a"=>"third", "b"=>[{"lt"=>9, "lg"=>10}]}=>1}
pp data.count_by(&:keys)
#=> {["a", "b"]=>4}
pp data.count_by{|key, value| key["a"]}
#=> {"first"=>2, "second"=>1, "third"=>1}
foos.map { |foo| {foo => foos.count(foo)} }.uniq
As I understand the question, it has nothing to do with the particular elements of your given array. More generally, given an array
arr = [a,a,b,c]
where a
, b
and c
are any Ruby objects, you want a count of each unique element, expressed as an array of hashes. To keep things simple, suppose
arr = [1,1,2,3]
Here are two ways to obtain the counts by making a single pass through arr
.
arr.group_by(&:itself).map { |k,v| { k=>v.size } }
#=> [{ 1=>2 }, { 2=>1 }, { 3=>1 }]
and (a counting hash , as used by @Eric in his answer)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
#=> [{ 1=>2 }, { 2=>1 }, { 3=>1 }]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.