Reputation: 105
I have these hashes as output which i need to push into an array without overwriting.
output1= {:user_id=>9, :project_id=>4, :task_id=>87, :comment=>"Test 20"}
output2 = {:user_id=>9, :project_id=>12, :task_id=>105,:comment=>"Test 21"}
I need to push these 2 output into a single array when I iterate through the loop. What is happening right now is that when I push the second output into the array it overwrites the first too and below is the result that I get.
Entry_array=[{:user_id=>9,:project_id=>12,:task_id=>105,:comment=>"Test 21"},
{:user_id=>9, :project_id=>12, :task_id=>105,:comment=>"Test 21"}]
I want the result of the hash output1 and hash output2 to be combined. Thanks appreciate all the help.
This is the code that i am using
attributes =[:user_id,:project_id,task_id,:comment]
entry_array=[]
output = {}
CSV.foreach(csv_file, headers: true, converters: :date).with_index do |row,line_no|
entry_hash= row.to_hash
.....some code here where we get the entry_hash....
i=0
entry_array <<output
end
Upvotes: 0
Views: 3207
Reputation: 11035
The reason this is happening, at least according to your code is because you are using the same output
hash for each row. If you ran
puts entry_array.collect(&:object_id)
at the end of your CSV file, you would see they are all the same object. So, even though you put it into the array at the end of each row, you are still modifying the same object, which the array is now pointing to. Essentially what you're doing is
a = { hello: 'world' } # => {:hello=>"world"}
b = a # => {:hello=>"world"}
b[:hello] = 'there'
a # => {:hello=>"there"}
b # => {:hello=>"there"}
# storing it in an array does the same thing
output = { hello: 'world' } # => {:hello=>"world"}
array = [output] # => [{:hello=>"world"}]
output[:hello] = 'there'
output # => {:hello=>"there"}
array # => [{:hello=>"there"}]
What you need to do to fix this is instantiate a new hash for each row:
attributes = [:user_id, :project_id, :task_id, :comment]
entry_array = []
CSV.foreach(csv_file, headers: true, converters: :date).with_index do |row, line_no|
output = { } # Instantiate here, inside the loop, so each row gets its own hash
entry_hash = row.to_hash
# if you need the key for something, use this
# entry_hash.each.with_index do |(key, value), index|
# otherwise, just iterate over each value
entry_hash.each_value.with_index do |value, index|
output[attributes[index]] = value.is_a?(Array) ? value.first.to_i : value
end
entry_array << output
end
I've changed your class check to an is_a?
and also removed the i
counter in favor of using with_index
over the iteration, you weren't using the key
in the sample shown, so I just used each_value
but have left a comment showing how to use each_index
with each
on a hash, if you were using the key
just not shown.
Upvotes: 2
Reputation: 10111
I am not sure this is the best way to make your dreams come true but it is a way
1) create your array
arr_of_hashes = []
2) Now that you have your array you can populate it with your hashes
output1= {:user_id=>9, :project_id=>4, :task_id=>87, :comment=>"Test 20"}
output2 = {:user_id=>9, :project_id=>12, :task_id=>105,:comment=>"Test 21"}
arr_of_hashes << output1
arr_of_hashes << output2
...
3) now when you inspect the value of your arr_of_hashes you will get
[{:user_id=>9, :project_id=>4, :task_id=>87, :comment=>"Test 20"}, {:user_id=>9, :project_id=>12, :task_id=>105, :comment=>"Test 21"}]
I hope that this helps:)
Happy Hacking
CSV.foreach(csv_file, headers: true, converters: :date).with_index do |row,line_no|
.....some code here where we get the entry_hash....
entry_array = [] # you need to define the element first before you can add stuff to it :)
entry_hash.each do |key,value|
if value.class == Array
output[attributes[i]] = value.first.to_i
else
output[attributes[i]] = value
end
i += 1
end
entry_array <<output
end
Upvotes: 0
Reputation: 1214
I think this is what you're looking for: https://apidock.com/ruby/Enumerator/each_with_index.
I guess the i
you're using gets flushed between iterations :).
Upvotes: 1