hash with array rails not chain value but delete - ruby-on-rails

I've tried to insert some Values into this hash were every key is an array but when I print all result just the last value
def self.hash_builder(query)
statistic = Hash.new { |hash, key| hash[key] = [] }
if !query.empty?
query.each do |q|
statistic[:sell].push(q.total_sell.to_i)
statistic[:price].push(q.total_price.to_f)
end
else
statistic[:sell].push(0)
statistic[:price].push(0.0)
end
return statistic
end
I call this method after make a query, and I send to this the query with the new params, but every time i see inside this hash just the last query value
THIS IS THE RESULT

I'll answer here because the comment section doesn't allow enough room. You're wrong about <<. It ought to work fine.
$ irb
irb(main):001:0> s = Hash.new {|h, k| h[k] = [] }
=> {}
irb(main):002:0> s[:sell] << 1
=> [1]
irb(main):003:0> s[:sell] << 2
=> [1, 2]
irb(main):004:0> s[:sell]
=> [1, 2]
irb(main):005:0> s[:price]
=> []
But push should work, too.
irb(main):006:0> s[:sell].push(3)
=> [1, 2, 3]

Related

Can someone please explain use of each_with_index, reduce, select chained in the code along with OR (||)?

Can someone explain this code to me? What does the || syntax mean and how does it work?
array = [1, 2, 3, 4, 5, 3, 6, 7, 2, 8, 1, 9]
array.each_with_index.reduce({}) { |hash, (item, index)|
hash[item] = (hash[item] || []) << index
hash
}.select{ |key, value| value.size > 1 }
I'd rewrite it this way
array.each_with_index.reduce(Hash.new { Array.new }) do |hash, (item, index)|
hash.merge(item => hash[item] << index)
end.select { |_, indexes| indexes.size > 1 }
We use each_with_index because we want to access the index while looping the array. You can see it later next to item as a parameter in the reduce's block.
reduce permits us to "transform" a collection in something else. In our case we want to construct an hash out of an array.
In the reduce's block we add the current index to the key value pair for the current item. I used merge to do it in just one expression (update the hash and using it as an expression to return).
In the end, we keep just the key value pairs whose values (and those are arrays) have more than one elements. Note that we don't care about keys here so I called the key parameter _.
array = [1, 2, 3, 4, 5, 3, 6, 7, 2, 8, 1, 9]
array.each_with_index.reduce({}) do |hash, (item, index)|
hash[item] = (hash[item] || []) << index
hash
end.select do |key, value|
value.size > 1
end
First of all cleaner way of writing Enumerators single line are enclosed with {} and multiline with do; end.
array.each_with_index.reduce({}) do |hash, (item, index)|
# hash[item] gets either itself and if itself is nil it gets an empty
# array assigned and additionally the index gets added to this array
hash[item] = (hash[item] || []) << index
# return the hash for the reduce enumerator
hash
end
In this part you iterate over the array and pass in the initial empty hash with .reduce({}). The hash then gets transformed and returned in L3 in the "loop" and passed into the next iteration of the reduce. Your result then is the build up hash which then immediately get enumerated with select where only key value pairs get returned that have a value size larger than 1.
Best would be to readup on Enumerators#reduce and how the objects are passed into the "loop".
hash[item] is nil at first. nil || [] => [], so hash[item] become array. << push an item into array.
hope this can help you understand how hash store the value
require "pp"
hash = {}
pp hash[1] # => nil
pp hash[1] || [] # => []
pp (hash[1] || []) << 1 # => [1]
pp hash[1] # => nil
hash[1] = (hash[1] || []) << 1
pp hash[1] # => [1]

What is the best way to access an element from 2d array saved as a hash value?

I have a hash, its values are 2 dimensional arrays, e.g.
hash = {
"first" => [[1,2,3],[4,5,6]],
"second" => [[7,88,9],[6,2,6]]
}
I want to access the elements to print them in xls file.
I did it in this way:
hash.each do |key, value|
value.each do |arr1|
arr1.each do |arr2|
arr2.each do |arr3|
sheet1.row(row).push arr3
end
end
end
end
Is there a better way to access each single element without using each-statement 4 times?
The desired result is to get each value from key-value pair as an array, e.g.
=> [1,2,3,4,5,6] #first loop
=> [7,88,9,6,2,6] #second loop
#and so on
hash = { "first" =>[[1, 2,3],[4,5,6]],
"second"=>[[7,88,9],[6,2,6]] }
hash.values.map(&:flatten)
#=> [[1, 2, 3, 4, 5, 6], [7, 88, 9, 6, 2, 6]]
Isn't it as simple as something like:
hash.each do |k,v|
sheet1.row(row).concat v.flatten
end

Ruby - "can't convert Symbol into Integer" when try to access data in array

Here's a sample of array:
{"C1"=>[
{:upc=>"51857195821952", :product_id=>"1234", :name=>"name", :price=>" $15 ", :color=>"green", :size=>"L", :description=>"descr"},
{:upc=>"352353wegs", :product_id=>"456", :name=>"name2", :price=>"$21", :color=>"black", :size=>"S", :description=>"descr"}, # ...
],
#...
}
And here as I am trying to fetch data from that array:
#array.each do |p|
product = Product.new
product.sku = p[0]
product.name = p[1][0][:name] #can't convert Symbol into Integer
price = p[1].select{ |pr| !pr[:price].nil? and pr[:price] != "0" }.min_by{ |i| i[:price].to_f }[:price]
product.price = "%.2f" % (price.to_f)
...
end
Every time I try to fetch data from the array, I get on the line product.name = the error can't convert Symbol into Integer.
What is wrong in this case? I spent a part of afternoon on this issue, but unfortunately I still cannot figure out it...
Thanky you
Your #array is actually a hash. It is formated like following:
{
'name1' => [{:upc => "..."},{:upc => "..."}],
'name2' => [{:upc => "..."},{:upc => "..."}],
#...
}
Since it is a Hash, you can use 2 arguments in the each (works for map also) method (one for the key, the other for the value):
#array.each do |name, array|
product = Product.new
product.sku = name # returns "C1"
array.each do |data|
data[:upc]
data[:name]
#etc...
end
end
The fundamental problem is that the sample array you showed above is not actually an array. It's a hash with key-value pairs. Therefore, your code like p[0] or p[1][0] doesn't make sense because a hash doesn't have index like array. Hash is not ordered. Hashes values are accessed with a "key" rather than an "index" like array.
Iterating through key-value pairs of a hash is done something like this.
1.9.3p194 :001 > x = {:x => 10, :y => 9, :z => 10}
=> {:x=>10, :y=>9, :z=>10}
1.9.3p194 :002 > x.each do |key, value|
1.9.3p194 :003 > puts "#{key} : #{value}"
1.9.3p194 :004?> end
x : 10
y : 9
z : 10
=> {:x=>10, :y=>9, :z=>10}
It looks like you may be confusing Arrays and Hashes a bit.
Given this:
#array = {"C1"=>[
{:upc=>"51857195821952", :product_id=>"1234", :name=>"name", :price=>" $15 ", :color=>"green", :size=>"L", :description=>"descr"},
{:upc=>"352353wegs", :product_id=>"456", :name=>"name2", :price=>" $21 ", :color=>"black", :size=>"S", :description=>"descr"}
] }
Then #array.class.name is Hash
You can get the actual array by accessing it like so:
#actual_array = #array["C1"]
Then, #actual_array.class.name will be Array
So, taking this approach and re-writing:
#array = {"C1"=>[
{:upc=>"51857195821952", :product_id=>"1234", :name=>"name", :price=>" $15 ", :color=>"green", :size=>"L", :description=>"descr"},
{:upc=>"352353wegs", :product_id=>"456", :name=>"name2", :price=>" $21 ", :color=>"black", :size=>"S", :description=>"descr"}
] }
#actual_array = #array["C1"]
#actual_array.each do |p|
puts p[:name]
end
If you do this, you'll find that the value of the :name element will be printed neatly out.

Creating a new hash with default keys

I want to create a hash with an index that comes from an array.
ary = ["a", "b", "c"]
h = Hash.new(ary.each{|a| h[a] = 0})
My goal is to start with a hash like this:
h = {"a"=>0, "b"=>0, "c"=>0}
so that later when the hash has changed I can reset it with h.default
Unfortunately the way I'm setting up the hash is not working... any ideas?
You should instantiate your hash h first, and then fill it with the contents of the array:
h = {}
ary = ["a", "b", "c"]
ary.each{|a| h[a] = 0}
Use the default value feature for the hash
h = Hash.new(0)
h["a"] # => 0
In this approach, the key is not set.
h.key?("a") # => false
Other approach is to set the missing key when accessed.
h = Hash.new {|h, k| h[k] = 0}
h["a"] # => 0
h.key?("a") # => true
Even in this approach, the operations like key? will fail if you haven't accessed the key before.
h.key?("b") # => false
h["b"] # => 0
h.key?("b") # => true
You can always resort to brute force, which has the least boundary conditions.
h = Hash.new.tap {|h| ["a", "b", "c"].each{|k| h[k] = 0}}
h.key?("b") # => true
h["b"] # => 0
You can do it like this where you expand a list into zero-initialized values:
list = %w[ a b c ]
hash = Hash[list.collect { |i| [ i, 0 ] }]
You can also make a Hash that simply has a default value of 0 for any given key:
hash = Hash.new { |h, k| h[k] = 0 }
Any new key referenced will be pre-initialized to the default value and this will avoid having to initialize the whole hash.
This may not be the most efficient way, but I always appreciate one-liners that reveal a little more about Ruby's versatility:
h = Hash[['a', 'b', 'c'].collect { |v| [v, 0] }]
Or another one-liner that does the same thing:
h = ['a', 'b', 'c'].inject({}) {|h, v| h[v] = 0; h }
By the way, from a performance standpoint, the one-liners run about 80% of the speed of:
h = {}
ary = ['a','b','c']
ary.each { |a| h[a]=0 }
Rails 6 added index_with on Enumerable module. This will help in creating a hash from an enumerator with default or fetched values.
ary = %w[a b c]
hash = ary.index_with(0) # => {"a"=>0, "b"=>0, "c"=>0}
Another option is to use the Enum#inject method which I'm a fan of for its cleanliness. I haven't benchmarked it compared to the other options though.
h = ary.inject({}) {|hash, key| hash[key] = 0; hash}
Alternate way of having a hash with the keys actually added
Hash[[:a, :b, :c].zip([])] # => {:a=>nil, :b=>nil, :c=>nil}
Hash[[:a, :b, :c].zip(Array.new(3, 0))] # => {:a=>0, :b=>0, :c=>0}

ruby hash setup

I found this in my some code I was working on and I was wondering what this is doing
h = Hash.new {|hash, key| hash[key] = 0}
=> {}
When a block is passed to Hash.new that block is called each time a non-existent key is accessed. Eg:
h = Hash.new { |hash, key| hash[key] = "Default" }
h[:defined_key] = "Example"
puts h[:defined_key] # => "Example"
puts h[:undefined_key] # => "Default"
See http://ruby-doc.org/core/classes/Hash.html#M000718 for more detail.
http://ruby-doc.org/core/classes/Hash.html#M000718
This block defines what the hash does when accessing a nonexistent key. So if there is no value for a key, then it sets the value to 0, and then returns 0 as the value.
It's not just good for defaults - you could have it throw an exception of there is no such key, for example. In fact, if you just want a default value, you can say:
Hash.new "defaultValue"
Its making the default values for any new keys equal to zero instead of nil, observe the test in and irb console session:
$ irb
>> normal_hash = Hash.new
=> {}
>> normal_hash[:new_key]
=> nil
>> h = Hash.new {|hash, key| hash[key] = 0}
=> {}
>> h[:new_key]
=> 0

Resources