Ruby/RoR - Count occurrence of an element in an array
I'v a hash
{1=>true, 7=>false, 6=>true, 4=>false}
or an array like
[1, true], [7, false], [6, true], [4, false]]
or
[true, false, true, false]
.
How can I find the number of true
s in the array?
In order to count the elements, you obviously have to iterate over the collection. Since iterating over a Hash
yields two-element Array
s, the first two are actually exactly the same:
{ 1 => true, 7 => false, 6 => true, 4 => false }.count(&:last)
[[1, true], [7, false], [6, true], [4, false]].count(&:last)
For the simple Array
case, you could do something like this:
[true, false, true, false].count(true)
This Array
is of course also the same as the Hash#values
from your Hash
above, so you could use the same method on that:
{ 1 => true, 7 => false, 6 => true, 4 => false }.values.count(true)
If you don't know which one of three you will get, you could use something like this:
{ 1 => true, 7 => false, 6 => true, 4 => false }.flatten.count(true)
[[1, true], [7, false], [6, true], [4, false]].flatten.count(true)
[true, false, true, false].flatten.count(true)
With Enumerable#count:
hash.values.count(true)
array_of_pairs.map { |k, v| v }.count(true)
plain_array.count(true)
More verbose, but does not create intermediate arrays:
hash_or_array_of_pairs.inject(0) { |acc, (k, v)| acc + (v == true ? 1 : 0) }
Simpler:
hash.values.count(true)
array.flatten.count(true)
This works with all the above cases.
Ruby 2.7+
Ruby 2.7 is introducing Enumerable#tally
(and possibly tally_by
) for this exact purpose. There's a good summary here.
There's a discussion about whether the final implementation will use tally
or tally_by
when providing a block, as here.
In this use case:
# or hash.tally_by if that's the accepted method for block form
hash.tally { |k, v| v }
# => { true => 2, false => 2 }
Docs on the features being released are here.
Hope this helps someone!
For hashes:
{ :a => true, :b => true, :c => false }.select{ |k,v| v }.length
=> 2
For arrays:
[true, false, false, true, true].select{ |o| o }.length
=> 3
Another way (testing with a negation):
[true, false, false, true, true].reject{ |o| o != true }.length
=> 3
One way (your hash would need .to_a
called on it first for this to work on it):
[[1, true], [7, false], [6, true], [4, false]].flatten.select{|s| s == true }.size
精彩评论