Reputation: 71
I've an array contains hashes, I want to filter few parameters from the hash and insert the filtered data in another array but am not succeed below is the sample data I've used
a = Array.new
a = [
{"name"=>"hello", "age"=>"12", "sex"=> "M", "city"=>"Chennai"},
{"name"=>"name2", "age"=>"26", "sex"=> "M", "city"=>"Banglore"}
]
line_item = Array.new
hash_data = {}
a.each do |datas|
hash_data[:name] = datas["name"]
hash_data[:age] = datas["age"]
line_item << hash_data
end
I am getting this result:
[
{:name=>"name2", :age=>"26"},
{:name=>"name2", :age=>"26"}
]
But am expecting this:
[
{:name=>"hello", :age=>"12"},
{:name=>"name2", :age=>"26"}
]
Somebody please help to sort out this, Thanks in advance
Upvotes: 2
Views: 2816
Reputation: 114138
Defining the hash outside the loop means that you keep adding the same hash object again (while overwriting its previous values). Instead, create a fresh hash within the loop:
line_items = []
a.each do |datas|
hash_data = {}
hash_data[:name] = datas["name"]
hash_data[:age] = datas["age"]
line_items << hash_data
end
The code looks a bit unidiomatic. Let's refactor it.
We can set the keys right within the hash literal:
line_items = []
a.each do |datas|
hash_data = { name: datas["name"], age: datas["age"] }
line_items << hash_data
end
We can get rid of the hash_data
variable:
line_items = []
a.each do |datas|
line_items << { name: datas["name"], age: datas["age"] }
end
And we can use map
to directly transform the array:
line_items = a.map { |h| { name: h["name"], age: h["age"] } }
#=> [{:name=>"hello", :age=>"12"}, {:name=>"name2", :age=>"26"}]
Upvotes: 7
Reputation: 23661
You can get the expected result with a combination of map
and slice
a = [
{"name"=>"hello", "age"=>"12", "sex"=> "M", "city"=>"Chennai"},
{"name"=>"name2", "age"=>"26", "sex"=> "M", "city"=>"Banglore"}
]
a.map{ |e| e.slice("name", "age") }
#=> [{"name"=>"hello", "age"=>"12"}, {"name"=>"name2", "age"=>"26"}]
map
: Returns Array
containing the values returned by blockslice
: Returns Hash
including only the specified keys Upvotes: 5
Reputation: 12203
I think a lot of people are over complicating this.
You can achieve this using the following:
a.map { |hash| hash.select { |key, _value| key == 'name' || key == 'age' } }
If you want to return an array, you should nearly always be using map
, and select
simply selects the key - value pairs that match the criteria.
If you're set on having symbols as the keys, you can call symbolize_keys
on the result.
I'll expand the code so it's a little more readable, but the one liner above works perfectly:
a.map do |hash|
hash.select do |key, _value|
key == 'name' || key == 'age'
end
end
Upvotes: 2
Reputation: 1680
If you want for all keys you can do this
array = [{"name"=>"hello", "age"=>"12", "sex"=> "M", "city"=>"Chennai"}, {"name"=>"name2", "age"=>"26""sex"=> "M", "city"=>"Banglore"}]
new_array = array.map{|b| b.inject({}){|array_obj,(k,v)| array_obj[k.to_sym] = v; array_obj}}
Ref: inject
Happy Coding
Upvotes: 1
Reputation: 787
In your loop you are essentially populating line_item
with hash_data
twice. This is the same object however. You can remedy this by using .dup
.
a.each do |datas|
hash_data[:name]=datas["name"]
hash_data[:age]=datas["age"]
line_item << hash_data.dup # <- here
end
irb(main):044:0> line_item
=> [{:name=>"hello", :age=>"12"}, {:name=>"name2", :age=>"26"}]
Edit: I prefer rado's suggestion of moving your definition of hash_data
inside the loop over using .dup
. It solves the problem more than treating the symptom.
Upvotes: 2
Reputation: 714
On the first line hash_data[:name]=datas["name"]
you are setting the key of the hash. That's why when the loop iterate again, it is overriding the value and after that push the new result to the hash.
One solution with reusing this code is just to put the hash_data = {}
on the first line of your loop. This way you will have a brand new hash to work with on every iteration.
Also I would recommend you to read the docs about the Hash module. You will find more useful methods there.
Upvotes: 1