Archie123
Archie123

Reputation: 105

Pushing hash into array

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

Answers (3)

Simple Lime
Simple Lime

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

MZaragoza
MZaragoza

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

medik
medik

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

Related Questions