21

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?

1
  • 2
    As of Ruby 2.7 you can use Enumerable#tally. More info here. Commented Jun 7, 2019 at 11:51

8 Answers 8

29

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)
Sign up to request clarification or add additional context in comments.

Comments

7

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

Comments

5

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!

Comments

4

Simpler:

hash.values.count(true)

array.flatten.count(true)

This works with all the above cases.

2 Comments

yeah, but in the case of the array with pairs, array.flatten.count(true) is conceptually weird, as it's taking in account also the keys (first item in pair). I know a true should never be found there, but still...
Well, if one was concerned about the keys possibly containing a true, as unlikely as it is, you could do this: Hash[array].values.count(true)
2

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

Comments

1

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

1 Comment

note that s == true could be simplified to s
0

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.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.