JesusJPG
JesusJPG

Reputation: 45

Trouble about iterating over an array to generate frequencies in a hash

I have an array with some different data (in string format) and I would like to count the frequencies of each value and store it in hash/dictonary, but i'm getting error trying to do it.

I would like to do something like this:

    words = ["foo", "var", "spam", "egg", "foo", "foo", "egg"]
    frequency = {}
    words.each{|word| frequency[word] += 1}

and get the following output:

   puts(frequency)   #{"foo" => 3, "var" => 1, "spam" => 1, "egg" => 2}

but I'm getting the following problem:

    prueba.rb:3:in `block in <main>': undefined method `+' for nil:NilClass (NoMethodError)

Is there another way of accomplishing the same result?

Upvotes: 1

Views: 46

Answers (4)

kiddorails
kiddorails

Reputation: 13014

words = ["foo", "var", "spam", "egg", "foo", "foo", "egg"]

words.each_with_object(Hash.new(0)) { |w, h| h[w] += 1 }
#=> {"foo"=>3, "var"=>1, "spam"=>1, "egg"=>2}

#each_with_object is the enumerator which iterates by passing each element with an object supplied (here Hash.new(0)) and returns that supplied object in next iteration.

Hash.new(0) creates a hash where default value of any key is 0.

hash = Hash.new(0)
hash['a'] #=> 0

We use this property of default value and simply pass each word to the object and increment the counter. :)

Issue in your code: You don't have default value in your case, so you would need to do that manually by checking if a key exists in the hash or not.

Upvotes: 1

eyevan
eyevan

Reputation: 1473

When you write:

words.each{|word| frequency[word] += 1}

It's equivalent to:

words.each{|word| frequency[word] = frequency[word] + 1}

However, frequency hash is empty and frequency[word] returns nil -- essentially, you are trying to do nil + 1, which results in the error you are getting.

You can initialize new elements of the hash manually:

words.each{|word| frequency[word] ||= 0; frequency[word] += 1}

Or, as other answers have already suggested, use frequency = Hash.new(0) as a shortcut.

Upvotes: 1

Muhammad Shazim Khan
Muhammad Shazim Khan

Reputation: 141

You can use this. By initializing it with Hash.new(0)

names = ["foo", "var", "spam", "egg", "foo", "foo", "egg"]

names.inject(Hash.new(0)) { |total, e| total[e] += 1 ;total}

# {"foo"=>3, "var"=>1, "spam"=>1, "egg"=>2}

Upvotes: 0

Ursus
Ursus

Reputation: 30056

If you query the hash for a key not present you get nil, that's the problem. Let's make 0 the default if a key is not present

frequency = Hash.new(0)

Upvotes: 4

Related Questions