Storing duplicate keys in hash - ruby

I am expecting duplicate key to be stored with value in student_hash, but due to absence of multimap in ruby, it is not allowed. One solution is to capture all this information in separate two arrays based on selection(id/type/school_id), but input data might have so much depth, and array size cannot be determined. Can the same behavior (expected output) be achieved using a hash? Please suggest.
Expected output:
{:id=>"101", :type=>"junior", :school_id=>"IS1.4598"}
{:id=>"103", :type=>"Senior", :school_id=>"IS1.098"}
Code:
require 'json'
raw_data='{
"Enquiry": "get_all_student_info",
"success": true,
"payload": [
{
"standard_version": "1.4",
"country_id": "USA_01",
"parent": "USA_IS1",
"id": "101",
"company": "Govt",
"type": "Junior",
"subsystem_type": "Govtgirlsschool",
"school_id": "IS1.098"
},
{
"standard_version": "1.4",
"country_id": "NZ_01",
"parent": "NZ_IS1",
"id": "103",
"company": "Private",
"type": "Senior",
"subsystem_type": "Govtboysschool",
"school_id": "IS1.098"
}
],
"error": ""
}'
def student_hash(set, result = {})
if set.class == Hash
set.each do |k, v|
if("#{k}"=="id" || "#{k}"=="type" || "#{k}"=="school_id")
puts result ["#{k}".to_sym] = "#{v}"
end
if v.class == Hash
result = student_hash(v, result)
elsif v.class == Hash || v.class == Array
result = student_hash(v, result)
end
end
elsif set.class == Array
set.each do |a|
result = student_hash(a, result)
end
end
result
end
student_hash(JSON.parse(raw_data))
# => {:id=>"103", :type=>"Senior", :school_id=>"IS1.098"}

Not to much clear but, This is enough to expected output for your raw_data
data = JSON.parse(raw_data)
data["payload"].map {|i| { :id => i["id"], type: i["type"], school_id: i["school_id"] }}
Now you can print hoe ever you want.

Try with below function, it will give result what you are expecting.
def content_display( result = [] )
student_array = []
#array = []
result["payload"].map {|i|
student_array={ :id => i["id"], :type => i["type"], :school_id => i["subsystem_instance_id"] }
#array << student_array
}
#array
end
array=content_display(JSON.parse(raw_data))
puts array

I think this may help you.
all_data = []
row = {:id=>"101", :type=>"junior", :school_id=>"IS1.4598"}
all_data << row
For example
students = Student.all
all_student = []
students.each do |student|
student_hash = {:id=>student.id, :type=>student.type, :school_id=>student.school_id}
all_student << student_hash
end
puts all_student
You will get result something like that
[{:id=>"101", :type=>"junior", :school_id=>"IS1.4598"}
{:id=>"103", :type=>"Senior", :school_id=>"IS1.098"}]

Related

Improvements in code having iteration of nested hashes and arrays of input

I could use any improvements to improve my code. I think most of the method have the same layout but I am not getting the desired output, so any help would be great. If you want to see the exercise online, its called the Bachelor Nested Iteration. I really have no clue why I am not getting my desired output, to me my working out makes sense I guess.
for the get_first_name_of_season_winner method, no matter what arguments I pass through when I call it, I always get "Beth Smalls" as an output when it shouldn't be the case. If I pass "Season 29", the output should be "Ashley Yeats"
for the get_contestant_name method, it's the same thing. It always returns "Beth Smalls" no matter what occupation I pass through. For example if I call it like this
get_contestant_name(thebachelor, "Chiropractic Assistant" )
it should return "Becca Tilley" as an output but it doesn't.
for the count_contestant_by_hometown, it should return the number of contestants which are from the hometown thats passed in the method, however, no matter which argument I pass, I get the number 4 as an output.
for the get_occupation, it should return the name of the person corresponding to the hometown being passed in the method, but I always get "Beth Smalls" no matter which hometown I pass through it.
The final method, I have no idea how to do it. It takes in two arguments––the data hash and a string of a season. Iterate through the hash and return the average age of all of the contestants for that season.
thebachelor = {
"season 30": [
{
"name": "Beth Smalls",
"age": "26",
"hometown": "Great Falls, Virginia",
"occupation": "Nanny/Freelance Journalist",
"status": "Winner"
},
{
"name": "Becca Tilley",
"age": "27",
"hometown": "Shreveport, Louisiana",
"occupation": "Chiropractic Assistant",
"status": "Eliminated Week 8"
}
],
"season 29": [
{
"name": "Ashley Yeats",
"age": "24",
"hometown": "Denver, Colorado",
"occupation": "Dental Assitant",
"status": "Winner"
},
{
"name": "Sam Grover",
"age": "29",
"hometown": "New York, New York",
"occupation": "Entertainer",
"status": "Eliminated Week 6"
}
]
}
Now the methods. get_first_name_of_season_winner is
def get_first_name_of_season_winner(data, season)
#this method returns the first name of that seasons winner
#pass the season of the show, and then it returns only th FIRST NAME of the winner for that season
#iterate through the inital hash to access the season number
#then iterate through the array, to access the hash inside
#acess the "status" to get the output
data.each do |season, contestant_Data|
contestant_Data.each do |a|
a.each do |attribute, value|
if value == "Winner"
return a[:name]
end
end
end
end
end
get_first_name_of_season_winner(thebachelor, "season 29") #returns the full name of only "Beth Smalls"
get_contestant_name is:
def get_contestant_name(data, occupation) #this method takes in the data hash and an occupation string and returns the name of the woman who has that occupation
#iterate through the initial hash to access the seasons
#iterate through the seasons to access the arrays inside
#access the occupation element of the array
#return the person who has the occupation
data.each do |season, contestant_data|
contestant_data.each do |a|
a.each do |attribute, value|
if attribute == :occupation
return a[:name]
end
end
end
end
end
get_contestant_name(thebachelor, "Chiropractic Assistant" ) #returns the full name of only "Beth Smalls"
count_contestant_by_hometown is:
def count_contestant_by_hometown(data, hometown) #this method should return the number of contestants from the hometown passed
#include a counter variable
#iterate through the hash to access the seasons
#access the array
#access the hometown key in the hash
#keep count
counter = 0
data.each do |season, contestant_data|
contestant_data.each do |a|
a.each do |attribute, value|
if attribute == :hometown
counter += 1
end
end
end
end
return counter
end
count_contestant_by_hometown(thebachelor, "Denver, Colorado") #returns the number 4, I have no idea why
get_occupation is:
def get_occupation(data, hometown) #should return the occupation of of the first contestant who hails from the hometown
data.each do |season, contestant_data|
contestant_data.each do |a|
a.each do |attribute, value|
if attribute == :hometown
return a[:name]
end
end
end
end
end
get_occupation(thebachelor, "Denver, Colorado") #returns "Beth Smalls" when it should return "Ashley Yeats"
average_age_for_season is:
def average_age_for_season(data, season) #returns the average age of all contestants for that season
I think a big problem comes from the data you're passing in. Take for example, a working solution for your final issue.
To get the data for a single season, you can use:
def average_age_for(data, season)
contestants = data[season]
contestants.sum { |contestant| contestant[:age].to_f } / contestants.count
end
average_age_for(thebatchelor, :"season 30")
#=> 26.5
Note that you need to pass :"season 30", rather than simply "season 30". That's because your data is is using symbolised strings as keys, rather than just strings.
Replace your data's keys with strings:
thebachelor = {
"season 30" => [
{
"name" => "Beth Smalls",
"age" => "26",
"hometown" => "Great Falls, Virginia",
"occupation" => "Nanny/Freelance Journalist",
"status" => "Winner"
},
{
"name" => "Becca Tilley",
"age" => "27",
"hometown" => "Shreveport, Louisiana",
"occupation" => "Chiropractic Assistant",
"status" => "Eliminated Week 8"
}
],
"season 29" => [
{
"name" => "Ashley Yeats",
"age" => "24",
"hometown" => "Denver, Colorado",
"occupation" => "Dental Assitant",
"status" => "Winner"
},
{
"name" => "Sam Grover",
"age" => "29",
"hometown" => "New York, New York",
"occupation" => "Entertainer",
"status" => "Eliminated Week 6"
}
]
}
Then look for a string in the method:
def average_age_for(data, season)
contestants = data[season]
# vvvvvvv
contestants.sum { |contestant| contestant["age"].to_f } / contestants.count
# ^^^^^^^
end
And this takes shape.
You can then do:
1)
def get_first_name_of_season_winner(data, season)
data[season].detect { |contestant| contestant["status"] == "Winner" }["name"].split.first
end
get_first_name_of_season_winner(thebachelor, "season 29")
#=> "Ashley"
2)
def get_contestant_name(data, occupation)
data.values.flatten.detect { |contestant| contestant["occupation"] == occupation }
end
get_contestant_name(thebachelor, "Chiropractic Assistant")
#=> {"name"=>"Becca Tilley", "age"=>"27", "hometown"=>"Shreveport, Louisiana", "occupation"=>"Chiropractic Assistant", "status"=>"Eliminated Week 8"}
3)
def count_contestant_by_hometown(data, town)
data.values.flatten.select { |contestant| contestant["hometown"] == town }.count
end
count_contestant_by_hometown(thebachelor, "New York, New York")
#=> 1
4)
def get_occupation(data, hometown)
data.values.flatten.detect { |contestant| contestant["hometown"] == hometown }["occupation"]
end
get_occupation(thebachelor, "New York, New York")
#=> "Entertainer"
Generalised && Optimised Solution:
Following method will work to get whatever you need from your hash thebachelor,
def get_information(data, required, season, optional, hash= {})
data = season.nil? ? data.values.flatten : data[season]
selected = data.select { |x| (hash.inject(true) { |m, (k,v)| m &&= (x[k] == v) }) }
required_data = selected.map { |x| x[required] }
if optional == :average && required == :age
(required_data.map(&:to_i).sum / required_data.count.to_f).round(2)
else
(optional == :count) ? required_data.count : required_data
end
end
Data need to be provided as below,
data - hash input from which you want to retrieve data
required - output inner hash attribute you want in output.
season - If you want to retrieve data season specific, provide season, else nil to get from all season.
Optional - It can be set nil, unless you want count or average. Otherwise pass :count or :average as optional argument.
hash - filter option. If you want to filter inner data by attributes like :hometown or :status or :occupation. Just provide it. otherwise it will set empty hash.
See examples below,
# Get name of season winner for season 'season 29'
get_information(thebachelor, :name, :'season 29', nil, status: 'Winner')
# => ["Ashley Yeats"]
# Get name of all winners irrespective of season
get_information(thebachelor, :name, nil, nil, status: 'Winner')
# => ["Beth Smalls", "Ashley Yeats"]
# Get contestant name for occupation "Chiropractic Assistant"
get_information(thebachelor, :name, nil, nil, occupation: "Chiropractic Assistant")
# => ["Becca Tilley"]
# Count contestant by home town "Denver, Colorado"
get_information(thebachelor, :name, nil, :count, hometown: "Denver, Colorado")
# => 1
# Get occupation of contestant who hails from hometown "Denver, Colorado"
get_information(thebachelor, :occupation, nil, nil, hometown: "Denver, Colorado")
# => ["Dental Assitant"]
# Get Average age for season :"season 29"
get_information(thebachelor, :age, :"season 29", :average)
# => 26.5
This method provide moreover than you asked in your question.
First, I would reformat your data so it's easy to select/detect:
data = data.map { |key, value| value.transform_keys(&:to_sym).merge(season: key) }
so now it looks like
[{
season: "season 30",
name: "Beth Smalls",
age: "26",
hometown: "Great Falls, Virginia",
occupation: "Nanny/Freelance Journalist",
status: "Winner"
},...
]
Now it's much easier to filter and detect:
def get_first_name_of_season_winner(data, season)
p = ->(v) { v[:season] == season && v[:status] == 'Winner' }
data.detect(&p)[:name][/\w+/]
end
def get_contestant_name(data, occupation)
p = ->(v) { v[:occupation] == occupation }
data.detect(&p)[:name]
end
def count_contestant_by_hometown(data, hometown)
p = ->(v) { v[:hometown] = hometown }
data.select(&p).count
end
def get_occupation(data, hometown)
p = ->(v) { v[:hometown] = hometown }
data.detect(&p)[:occupation]
end
def average_age_for_season(data, season)
p = ->(v) { v[:season] = season }
ages = data.select(&p).map { |datum| datum[:age] }
ages.sum.fdiv(ages.count) unless ages.empty?
end
In general, all these problems are of two types:
1. Given an array of data, find all the items that satisfy a certain condition
2. Given an array of data, find the first item that satisfies a certain condition
And you always solve them with select/detect and a block/proc.

Merge array of hashes by some keys and sum values of other keys

I have a array like
array = [
{"point"=>6, "score"=>4, "team"=>"Challenger"},
{"point"=>4, "score"=>2, "team"=>"INB"},
{"point"=>2, "score"=>2, "team"=>"Super-11"},
{"point"=>3, "score"=>7, "team"=>"INB"}
]
I want to merge hashes by "team" and sum the values of "point" and "score". Additionally want to insert an key "qualified" in each hash if point is greater than 5. So the final result will be:
result= [
{"point"=>6, "score"=>4, "qualified"=> "yes", "team"=>"Challenger"},
{"point"=>7, "score"=>9, "qualified"=> "yes", "team"=>"INB"},
{"point"=>2, "score"=>2, "qualified"=> "no", "team"=>"Super-11"}
]
Any help would be appreciated. Thanks!
One more possible solution :)
array.group_by { |item| item['team'] }.map do |_, items|
result = items.inject({}) { |hash, item| hash.merge(item) { |_, old, new| Integer(old) + new rescue old } }
result.merge("qualified" => result['point'] > 5 ? "yes" : "no")
end
Combination of group_by and map should help
result =
array.group_by {|item| item['team'] }
.map do |team, items|
total_points = items.map{|item| item['point']}.reduce(0, :+)
total_score = items.map{|item| item['score']}.reduce(0, :+)
qualified = points > 5
{
'point' => total_points,
'score' => total_score,
'qualified' => qualified ,
'team' => team
}
end
result = array.group_by{|i| i['team']}
.map do |k,v|
points = v.map{|i| i['point']}.inject(0, :+)
score = v.map{|i| i['score']}.inject(0, :+)
{
'point' => points,
'score' => score,
'qualified' => points > 5 ? 'yes' : 'no',
'team' => k
}
end
This is an alternative version. group_by is mandatory, I guess.
I used a temporary hash with keys as symbol to store data during iterations.
result = array.group_by { |hash| hash['team'] }.map do |team|
tmp_hash = {point: 0, score: 0, team: team[0], qualified: 'no'}
team[1].each { |h| tmp_hash[:point] += h['point'] ; tmp_hash[:score] += h['score'] }
tmp_hash[:qualified] = 'yes' if tmp_hash[:point] > 5
tmp_hash
end
this gives as result:
# => [
# {:point=>6, :score=>4, :team=>"Challenger", :qualified=>"yes"},
# {:point=>7, :score=>9, :team=>"INB", :qualified=>"yes"},
# {:point=>2, :score=>2, :team=>"Super-11", :qualified=>"no"}
# ]
After doing group_by, a simple map operation which takes the first element as the mapped value, sums up point and score within it and then merges the qualified condition into it is easy enough:
array
.group_by { |h| h["team"] }
.map do |_, a|
["point", "score"].each { |k| a.first[k] = a.sum { |h| h[k] } }
a.first.merge({"qualified": a.first["score"] > 5 ? 'yes' : 'no'})
end
Online demo here
array.each_with_object({}) do |g,h|
h.update(g["team"]=>g.merge("qualified"=>g["score"] > 5 ? "yes" : "no")) do |_,o,n|
{ "point" =>o["point"]+n["point"],
"score" =>o["score"]+n["score"],
"team" =>o["team"],
"qualified"=>(o["score"]+n["score"]) > 5 ? "yes" : "no" }
end
end.values
#=> [{"point"=>6, "score"=>4, "team"=>"Challenger", "qualified"=>"no"},
# {"point"=>7, "score"=>9, "team"=>"INB", "qualified"=>"yes"},
# {"point"=>2, "score"=>2, "team"=>"Super-11", "qualified"=>"no"}]
This uses the form of Hash#update (aka merge!) that employs a block to determine the values of keys (here the value of :id) that are present in both hashes being merged. See the doc for the description of the three block variables (here _, o and n).
Note that the receiver of values (at the end) is
{"Challenger"=>{"point"=>6, "score"=>4, "team"=>"Challenger", "qualified"=>"no"},
"INB"=>{"point"=>7, "score"=>9, "team"=>"INB", "qualified"=>"yes"},
"Super-11"=>{"point"=>2, "score"=>2, "team"=>"Super-11", "qualified"=>"no"}}
One could alternatively make a separate pass at the end to add the key "qualified':
array.each_with_object({}) do |g,h|
h.update(g["team"]=>g) do |_,o,n|
{ "point" =>o["point"]+n["point"],
"score" =>o["score"]+n["score"],
"team" =>o["team"] }
end
end.values.
map { |h| h.merge("qualified"=>(h["score"] > 5) ? "yes" : "no") }

Array with hash, how to merge same keys and add its value

I have an array with hashes in it. If they have the same key I just want to add its value.
#receivers << result
#receivers
=> [{:email=>"user_02#yorlook.com", :amount=>10.00}]
result
=> {:email=>"user_02#yorlook.com", :amount=>7.00}
I want the result of above to look like this
[{:email=>"user_02#yorlook.com", :amount=>17.00}]
Does anyone know how to do this?
Here is the the entire method
def receivers
#receivers = []
orders.each do |order|
product_email = order.product.user.paypal_email
outfit_email = order.outfit_user.paypal_email
if order.user_owns_outfit?
result = { email: product_email, amount: amount(order.total_price) }
else
result = { email: product_email, amount: amount(order.total_price, 0.9),
email: outfit_email, amount: amount(order.total_price, 0.1) }
end
#receivers << result
end
end
Using Enumerable#group_by
#receivers.group_by {|h| h[:email]}.map do |k, v|
{email: k, amount: v.inject(0){|s,h| s + h[:amount] } }
end
# => [{:email=>"user_02#yorlook.com", :amount=>17.0}]
Using Enumerable#each_with_object
#receivers.each_with_object(Hash.new(0)) {|h, nh| nh[h[:email]]+= h[:amount] }.map do |k, v|
{email: k, amount: v}
end
# Output: [{ "em#il.one" => 29.0 }, { "em#il.two" => 39.0 }]
def receivers
return #receivers if #receivers
# Produces: { "em#il.one" => 29.0, "em#il.two" => 39.0 }
partial_result = orders.reduce Hash.new(0.00) do |result, order|
product_email = order.product.user.paypal_email
outfit_email = order.outfit_user.paypal_email
if order.user_owns_outfit?
result[product_email] += amount(order.total_price)
else
result[product_email] += amount(order.total_price, .9)
result[outfit_email] += amount(order.total_price, .1)
end
result
end
#receivers = partial_result.reduce [] do |result, (email, amount)|
result << { email => amount }
end
end
I would just write the code this way:
def add(destination, source)
if destination.nil?
return nil
end
if source.class == Hash
source = [source]
end
for item in source
target = destination.find {|d| d[:email] == item[:email]}
if target.nil?
destination << item
else
target[:amount] += item[:amount]
end
end
destination
end
usage:
#receivers = []
add(#receivers, {:email=>"user_02#yorlook.com", :amount=>10.00})
=> [{:email=>"user_02#yorlook.com", :amount=>10.0}]
add(#receivers, #receivers)
=> [{:email=>"user_02#yorlook.com", :amount=>20.0}]
a = [
{:email=>"user_02#yorlook.com", :amount=>10.0},
{:email=>"user_02#yorlook.com", :amount=>7.0}
]
a.group_by { |v| v.delete :email } # group by emails
.map { |k, v| [k, v.inject(0) { |memo, a| memo + a[:amount] } ] } # sum amounts
.map { |e| %i|email amount|.zip e } # zip to keys
.map &:to_h # convert nested arrays to hashes
From what I understand, you could get away with just .inject:
a = [{:email=>"user_02#yorlook.com", :amount=>10.00}]
b = {:email=>"user_02#yorlook.com", :amount=>7.00}
c = {email: 'user_03#yorlook.com', amount: 10}
[a, b, c].flatten.inject({}) do |a, e|
a[e[:email]] ||= 0
a[e[:email]] += e[:amount]
a
end
=> {
"user_02#yorlook.com" => 17.0,
"user_03#yorlook.com" => 10
}

Ruby map a hash and extract the keys with values if nested or not

I’m using the PaperTrail gem to track changes in my Job model. Each time I change a record, the object is serialized and stored inside a changeset field on my Version model.
To display the values that have changed, I’m deserializing the object and iterating through it using map. I need help creating a method that extracts all the keys and values from this hash, regardless of whether they’re nested or not. The nesting will never be more than one level deep.
Here is my current code:
# deserializing the object into a hash
json_string = v.changeset.to_json
serialized_json = JSON.parse(json_string)
# current method to extract keys with values, but doesn’t pick up nested ones
serialized_json.map { |k,v|
puts k.titleize
puts v[1].present? ? v[1] : '<empty>'
}
# serialized_json (1st output)
{
"name" => [
[0] nil,
[1] "Epping Forest"
],
"job_address" => [
[0] nil,
[1] "51 Epping Forest, Essex, ES1 SAD"
]
}
# serialized_json (2nd output)
{
"quote" => [
[0] {
"quote" => {
"url" => nil
}
},
[1] {
"quote" => {
"url" => "/tmp/uploads/1433181671-22986-9554/file.pdf"
}
}
]
}
The values I’d like to extract from my method are:
"Name"
"Epping Forest"
"Quote Url"
"/tmp/uploads/1433181671-22986-9554/file.pdf"
In your below code, I assume you're putting [0] and [1] as index numbers to help the readers of your code, as it is not valid Ruby syntax.
"name" => [
[0] nil,
[1] "Epping Forest"
],
As for extracting 1 level deep keys and values, since it's a bit unclear to me how you would like your output formatted, I'ma ssuming a 1-D array is fine...in which case, something like this would give you the keys and values:
serialized_json hash w/valid Ruby syntax
{
"name" => [
nil,
"Epping Forest"
],
"job_address" => [
nil,
"51 Epping Forest, Essex, ES1 SAD"
]
}
Then:
serialized_json.map { |k, v| [k, v] }.flatten.reject { |x| x.nil? }
outputs:
["name", "Epping Forest", "job_address", "51 Epping Forest, Essex, ES1 SAD"]
I hope this helps :)
#str = ""
def hash_checks(hash)
hash.each do |k,v|
if v.is_a?(Array)
v.each_with_index do |element, index|
if element.is_a?(Hash)
#str = element[index] + #str if hash_check(element)
else
#str << k.titleize + "\n" + element + "\n" unless element.nil?
end
end
elsif v.is_a?(Hash)
#str << k.titleize if hash_check(v)
end
end
puts #str
end
def hash_check(hash)
add_tag = false
hash.each do |k,v|
if v.is_a?(Hash)
#str = k.titleize + " " + #str if hash_check(v)
else
#str << k.titleize + "\n" + v + "\n" unless v.nil?
add_tag = true
end
end
add_tag
end
I've excluded the '<empty>' value for nil because the data you would want to extract is the one that matters.
There's an inconsistency on the data you want though, since on your first serialized json, there are 2 non-nil values, but you just want one.
This method includes both, and runs even for multi-level nests :)

How effectively join ruby hashes recieved from json lists

I'm trying to make page with table which content is data from two arrays.
I have two lists(arrays) with hashes:
arr1 = [
{ "device"=>100, "phone"=>"12345" },
...,
{ "device"=>102, "phone"=>"12346" }
]
arr2 = [
{ "device"=>100, "type"=>"mobile", "name"=>"nokia" },
...,
{ "device"=>102, "type"=>"VIOP", "name"=>"smth" }
]
How can I join hashes from arr1 and arr2 by "device" to get a result array:
result = [
{ "device"=>100, "phone"=>"12345", "type"=>"mobile", "name"=>"nokia" },
...,
{ "device"=>102, "phone"=>"12346", "type"=>"VIOP", "name"=>"smth" }
]
Page which consist table with result array, loads very slowly and I need to find the fastest way to generate result_array.
Help me please.
This would work:
(arr1 + arr2).group_by { |i| i["device"] }.map { |d,(i1,i2)| i1.merge(i2)}
#=> [{"device"=>100, "phone"=>"12345", "type"=>"mobile", "name"=>"nokia"}, {"device"=>102, "phone"=>"12346", "type"=>"VIOP", "name"=>"smth"}]
Multiple ways to tackle it. Here is a quite readable way to do it:
# prepare an index hash for easier access of data by device
first_by_device = arr1.group_by {|a| a['device'] }
# build a new array joining both data hashes for each item
result = arr2.map do |item|
device = item['device']
item.merge first_by_device(device)
end
(arr1 + arr2).group_by { |i| i["device"] }.values.map{|x|x.reduce(&:merge)}
Maybe it's not the prettiest one, but it works:
result = arr1.collect{|a| h = arr2.select{|b| b["device"] ==
a["device"]}.first; h ? a.merge(h) : a }
Do you need something faster for large amount of data?
h = Hash.new
arr1.each do |a|
h[ a["device" ] ] ||= Hash.new
h[ a["device" ] ].merge!(a)
end
arr2.each do |a|
h[ a["device" ] ] ||= Hash.new
h[ a["device" ] ].merge!(a)
end
result = h.values

Resources