adding to the value in a hash table - ruby

I am trying to increment the value in a hash by one. My logic seems right, but for some reason my value in the hash is not incrementing by one.
puts item_sold
temp = sales_hash.values[item_sold] + 1
sales_hash.values[item_sold] = temp
puts sales_hash.values[item_sold]
sales_hash is a hash where the key is a number between 1000-2000 and the value for each key starts at 0. item_sold is a random number between 1 and 15. There are 15 items in the hash. When temp prints out it is a value of one. However when I print out the value of sales_hash.values[item_sold] it prints 0. What is sales_hash.values[item_sold] not incrementing?

Hash#values returns an array of of all the hashes values. You want to add to one value, which you'd do like this:
item_sold
=> {0=>0, 1=>0, 2=>0}
item_sold[0] += 1
=> 1
item_sold
=> {0=>1, 1=>0, 2=>0}
You access the value of a hash by by using the hash[key] syntax.

Related

Initializing hash with default value and incrementing by 1

I need a hash whose keys should have default value 0. (basically I'm making a counter). Keys are not known so I cannot initialize them in beginning. Also with every occurrence of the key, the value should increase by 1.
I have come up with this:
hash = {}
hash[key] ? hash[key]+=1 : hash[key]=0
This looks OK and short, but I don't like repeating hash[key] so many times in one line of code. Is there a better way to write this?
I think all you need is to give the hash a default value of 0
hash = Hash.new(0)
then for every occurrence of the key, you don't need to check its value, just increment it directly:
hash[key]+=1
Reference: Hash#new.
Look at Hash#default:
=> h = { }
=> h.default = 0
=> h["a"]
#> 0
=> h["z"]
#> 0

Iterate over array of arrays

This has been asked before, but I can't find an answer that works. I have the following code:
[[13,14,16,11],[22,23]].each do |key,value|
puts key
end
It should in theory print:
0
1
But instead it prints:
13
22
Why does ruby behave this way?
Why does ruby behave this way?
It's because what actually happens internally, when each and other iterators are used with a block instead of a lambda, is actually closer to this:
do |key, value, *rest|
puts key
end
Consider this code to illustrate:
p = proc do |key,value|
puts key
end
l = lambda do |key,value|
puts key
end
Using the above, the following will set (key, value) to (13, 14) and (22, 23) respectively, and the above-mentioned *rest as [16, 11] in the first case (with rest getting discarded):
[[13,14,16,11],[22,23]].each(&p)
In contrast, the following will spit an argument error, because the lambda (which is similar to a block except when it comes to arity considerations) will receive the full array as an argument (without any *rest as above, since the number of arguments is strictly enforced):
[[13,14,16,11],[22,23]].each(&l) # wrong number of arguments (1 for 2)
To get the index in your case, you'll want each_with_index as highlighted in the other answers.
Related discussions:
Proc.arity vs Lambda.arity
Why does Hash#select and Hash#reject pass a key to a unary block?
You can get what you want with Array's each_index' method which returns the index of the element instead of the element itself. See [Ruby'sArray` documentation]1 for more information.
When you do:
[[13,14,16,11],[22,23]].each do |key,value|
before the first iteration is done it makes an assignment:
key, value = [13,14,16,11]
Such an assignment will result with key being 13 and value being 14. Instead you should use each_with_index do |array, index|. This will change the assignment to:
array, index = [[13,14,16,11], 0]
Which will result with array being [13,14,16,11] and index being 0
You have an array of arrays - known as a two-dimensional array.
In your loop, your "value" variable is assigned to the first array, [13,14,16,11]
When you attempt to puts the "value" variable, it only returns the first element, 13.
Try changing puts value to puts value.to_s which will convert the array to a string.
If you want every value, then add another loop block to your code, to loop through each element within the "value" variable.
[[1,2,3],['a','b','c']].each do |key,value|
value.each do |key2,value2|
puts value2
end
end

Ruby Hash explanation

I do not understand this particular step in CodeAcademy.
text = puts "Hello text please"
text = gets.chomp
words = text.split(' ')
frequencies = Hash.new(0)
words.each { |x| frequencies[x] += 1 }
The idea is to filter the input to return a hash with each word and the amount of times the word appears. Having trouble understanding why this works.
words.each { |x| frequencies[x] += 1 }
Doesn't hash work by a {key, value} method?
The syntax for setting hash value is:
hash_name[key] = value
And the value is referenced as hash_name[key]. So:
frequencies = Hash.new(0)
This creates a new hash which, if you read the value of the hash for an unknown key, it will allow it and default the key's value as 0 (returns a 0). Without the 0 parameter, there would be no default key value, so that reading the hash with an unknown key would yield nil. But with the default return value of 0, the following:
words.each { |x| frequencies[x] += 1 }
Takes advantage of the default by going through all of the words, using them as keys, even though they don't initially exist, and incrementing the hash value of frequency[x] for the hash key x (the current word). If it hasn't been set yet, it starts at 0 which is what you want to count things. This because += will really mean frequencies[x] = frequencies[x] + 1 and the initial value returned for frequencies[x] when the value hasn't been set yet will be 0.
I'm not sure exactly where your problem lies, but hopefully this will help.
Doesn't hash work by a {key, value} method?
Yes it does. In the line
words.each { |x| frequencies[x] += 1 }
the hash is called frequencies and the key is x. The value for that key is returned by the expression frequencies[x].
It's just like an array, but using strings as indices instead of integers. data[2] is the value stored at the element of array data identified by 2, while frequencies[x] is the value stored at the element of hash frequencies indicated by x.
+= has its usual meaning as a Ruby abbreviation, so that var += 1 is identical to var = var + 1.
So frequencies[x] += 1 is frequencies[x] = frequencies[x] + 1: it adds one to the current value of the hash element identified by x.
The last piece in the puzzle is the way frequencies has been created. Ordinarily, accessing a hash element that hasn't been assigned returns nil. Using += would usually raise an undefined method '+' for nil:NilClass error because there is no method NilClass#+. But using Hash.new(0) creates a hash with a default value of zero, so that non-existent elements of this hash evaluate as 0 instead of nil, and now everything works fine when you try to increment an element for the first time.

Filter hash by key and increase corresponding values

I have a hash: date => integer. I need to change the values, but only for keys, satisfying the requirement: date1 < key < date2. What is an elegant way to do it?
Try something like:
my_hash.each {|key, value| my_hash[key] += 1 if date1 < key and key < date2 }
So what you want to do is reduce over your hash. You will iterate through your hash and check each key, if it meets the conditions you will put that key into a new hash with a new value, modified however you would like. Otherwise, the key goes into the new hash with the old value.
hash.reduce({}) do |memo,iter|
if iter[0] < Time.now
memo[iter[0]] = iter[1] + 1
else
memo[iter[0]] = iter[1]
end
memo
end
What this says is iterate over the hash. When you iterate over the hash with a reduce, it passes each pair [key,value] to the block. Thus, iter[0] is the key, iter[1] the value. Now we test on the key, if the condition is satisfied change the value and put the key and new value into the new hash. Otherwise, put the key and old value into the hash. The modified new hash is returned for the next iteration of the reduce. Hope this helps.

syntax for .max/.min and sum of hash values

Trying to get a value out of 2d array inside of a hash and determine its max or min value
This is what I have so far
pretend_hash = { 333 => [Dog,19.99], 222=> [Cat,25.55] }
if a == 5 # Loop for view highest priced product"
puts "View highest priced product"
puts pretend_hash.values.max
end
So this returns the highest alphabetical value in the first part of the array by default
Which would be Dog. How do I go about getting access to the 2nd part of the array where 25.55 gets declared the .max value? Something like pretend_hash.values.max[|k,v,z| print z]
or a reverse on something?
The other problem I'm having is iterating through 2nd hash elements and determining the sum. Again callling that 2nd element in the array is something I'm not able to find the syntax for. I mean its not hard to say 19.99+25.55 = some variable and slam it into a puts. I'm assuming its something like:
pretend_hash.sum{|k,v,z| ?? }
#I assume it iterates through the z element
#and adds all current z values and returns the sum?
Min/max can be solved like this:
pretend_hash.values.sort{|x,y| x[1] <=> y[1]}[0] # Gives min, -1 will be max
Or:
pretend_hash.values.map{|x| x[1]}.max
And sum can be had like this:
pretend_hash.values.inject(0){|sum,x| sum+x[1]}
pretend_hash = { 333 => ["Dog",19.99], 222=> ["Cat",25.55] }
key,value = pretend_hash.max{|a,b| b[1] <=> a[1]}
puts key.to_s
puts value.join(',')
puts pretend_hash.inject(0){|sum, hash| sum + hash[1][1]}.to_s
#returns:
#222
#Cat,25.55
#45.54

Resources