6

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 5

10

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/

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

0

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.

1 Comment

Simone_, with regard to the hash count that you've created (and in other similar situations), consider using each_with_object rather than inject. By doing so, you don't need to return the object you've created to the iterator (counter at the end). Also, if you create the hash with new values defaulting to zero, you don't need check to see if a key already exists. Apply these two suggestion here, we get: array.each_with_object(Hash.new(0)) {|item, counter| counter[item.downcase] += 1}
0

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

Comments

0

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}

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.