Mithun Sreedharan
Mithun Sreedharan

Reputation: 51292

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 trues in the array?

Upvotes: 21

Views: 16507

Answers (8)

Jibran Usman
Jibran Usman

Reputation: 401

Hash:

my_hash.values.select(&:present?).size

2D array:

my_2d_array.map(&:last).select(&:present?).size

or

my_2d_array.select { |_first, last| last.present? }.size

1D array:

array.select(&:present?).size

Upvotes: 0

Cadoiz
Cadoiz

Reputation: 1676

I'm trying to provide a more general answer: count the amount of each element. I'm still using 2.6.6, so somebody else also has no 2.7.x. Preparation:

values_of_hash = {1=>true, 7=>false, 6=>true, 4=>false}.values # also works with the `.map` command from the line below
values_of_array_of_pairs = [[1, true], [7, false], [6, true], [4, false]].map { |k, v| v } # or the `.inject` command from this answer: https://stackoverflow.com/a/4488905/4575793
values_of_2d = [[1, true], [7, false], [6, true], [4, false]].map(&:last) # similar idea from https://stackoverflow.com/a/4490032/4575793
values_of_2d = {1=>true, 7=>false, 6=>true, 4=>false}.map(&:last) # both a hash and an array of pairs hold the second value in `.last`
values_of_array = [true, false, true, false]

Now you can just use

def my_tally(values)
  Set.new(values).map {|unique_value| [unique_value, values.count(unique_value)]}
end
# replace `_xy` with anything of the above like e.g. `_array`
my_tally values_of_xy
# => [[true, 2], [false, 2]]

That also works for arbitrary data. Maybe you want to analyse this:

values_of_xy = {1=>:banana, 2=>:cherry, 3=>:apple, 4=>:cherry, 5=>:banana, 6=>:cherry}.values
my_tally values_of_xy
# => [[:banana, 2], [:cherry, 3], [:apple, 1]]

For Ruby 2.7+, you should obviously use tally.

Upvotes: 0

SRack
SRack

Reputation: 12213

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!

Upvotes: 5

tokland
tokland

Reputation: 67900

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) }

Upvotes: 7

Jörg W Mittag
Jörg W Mittag

Reputation: 369614

In order to count the elements, you obviously have to iterate over the collection. Since iterating over a Hash yields two-element Arrays, 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)

Upvotes: 29

Mark Thomas
Mark Thomas

Reputation: 37527

Simpler:

hash.values.count(true)

array.flatten.count(true)

This works with all the above cases.

Upvotes: 4

idlefingers
idlefingers

Reputation: 32067

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

Upvotes: 1

Ivan Torres
Ivan Torres

Reputation: 1739

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

Upvotes: 2

Related Questions