Ruby Count Array objects if object includes value

Viewed 9296

I have an array:

array = ['Footballs','Baseball','football','Soccer']

and I need to count the number of times Football or Baseball is seen, regardless of case and pluralization.

This is what I tried to do, but with no luck:

array.count { |x| x.downcase.include? 'football' || x.downcase.include? 'baseball' }

What is a right or better way to write this code? I am looking for 3 as an answer.

5 Answers

I would use count combined with a block that checks each element against a regular expression that matches the constraints you're looking for. In this case:

array.count { |element| element.match(/(football|baseball)s?\Z/i) }

This will match any of these elements: football, footballs, baseball, baseballs.

The s? makes the 's' optional, the i option (/i) makes the expression case insensitive, and the \Z option checks for the end of the string.

You can read more about Regexps in the Ruby docs: http://www.ruby-doc.org/core-2.0.0/Regexp.html

A great tool for playing with Regexps is Rubular: http://rubular.com/

If you give a block to the count method of array, it iterates over the array and counts the values for which you return true:

array.count do |x|
  (x.downcase.include? 'footbal') || (x.downcase.include? 'baseball')
end

You can use inject to count each item and return the result.

array = ['Football','Baseball','football','Soccer']

count = array.inject({}) do |counter, item|
  counter[item.downcase] ||= 0
  counter[item.downcase]  += 1
  counter
end

# => {"football"=>2, "baseball"=>1, "soccer"=>1} 

If you need to count a single value, it's even simpler.

array = ['Football','Baseball','football','Soccer']

count = array.inject(0) do |counter, item|
  counter += (item.downcase == 'football' ? 1 : 0)
end

On one line

array = ['Football','Baseball','football','Soccer']

count = array.inject(0) { |counter, item| counter += (item.downcase == 'football' ? 1 : 0) }

To include pluralization, simply enhance the comparison.

Assuming you have Ruby on Rails installed for the singularize method (you don't actually need to run this in rails):

require 'active_support/inflector'

array = ['Footballs','Baseball','football','Soccer']
uniq = array.map { |s| s.downcase.singularize }.uniq

uniq.size # => 3

Using Rails and Ruby >= 2.7 you can do:

array = ['Footballs','Baseball','football','Soccer']
array.map(&:downcase).map(&:singularize).tally
=> {"football"=>2, "baseball"=>1, "soccer"=>1}
Related