ruby sort hash based on another hash - ruby

I have the following two hashes:
db = {"1" => "first_name", "2" => "last_name", "5" => "status", "10" => "city" }
csv = {"1" => "first_name", "2" => "last_name", "5" => "status", "7" => "address", "10" => "city" }
I want to order csv based on db, and if there are any keys in csv not in db, then I want to move them to the end of csv, so in the above example the result would look like this:
{"1" => "first_name", "2" => "last_name", "5" => "status", "10" => "city", "7" => "address" }
Since the key "7" wasn't in db hash, we just moved it to the end of the csv hash.
This is what I tried:
db = {"1" => "first_name", "2" => "last_name", "5" => "status", "10" => "city" }
csv = {"1" => "first_name", "2" => "last_name", "5" => "status", "7" => "address", "10" => "city" }
rejects = csv.reject {|k| db.include? k }
result = csv.keep_if {|k,_| db.include? k }
result.merge!(rejects)
result
=> {"1"=>"first_name", "2"=>"last_name", "5"=>"status", "10"=>"city", "7"=>"address"}
It seems to work. But is it guaranteed to work? Will the merger always put the second hash at the end, or is there a possibility that the merger could mix the hashes together without consideration of order?

You could do the following:
db_keys = db.keys
#=> ["1", "2", "5", "10"]
keys = db_keys + (csv.keys-db_keys)
#=> ["1", "2", "5", "10", "7"]
Hash[keys.zip(csv.values_at(*keys))]
#=> { "1"=>"first_name", "2"=>"last_name", "5"=>"status",
# "10"=>"city", "7"=>"address"}

Related

Outputting the keys of the max value of a hash within Arrays in Ruby using methods

data = [
"Company one" => {
"number_1" => 46,
"number_2" => 3055,
"country" => "USA"
},
"Company two" => {
"number_1" => 32,
"number_2" => 6610,
"country" => "USA"
},
"Company three" => {
"number_1" => 40,
"number_2" => 9128,
"country" => "USA"
}
]
So I have this array in which I'm trying to get which of the company has the biggest number in 'number_2'. The largest would be Company three with 9128.
So I have this code that puts the largest number which would be 9128
def number(data)
collected_array=[]
data.each do |company_hash|
collected_array = company_hash.map do |k,v|
v["number_2"]
end
end
puts collected_array.max
end
number(data)
But I'm trying to puts the company name with the largest number which would be "Company three". I've tried .keys and other ways but it gives me error.
I've tried this way:
def number(data)
collected_array=[]
data.each do |company_hash|
collected_array = company_hash.map do |k,v|
v["number_2"]
k
end
end
puts collected_array.max
end
number(data)
but it gives me "Company two" rather than "Company three" which would be the company with the highest number
As stated by #Cary, it can be simplified accessing the first element on data, and there using max_by, on the hash local variable available within the block checking the number_2 key value.
As the result is an Array containing two elements, the first one is the company name, the second and last one, the hash containing its data:
data = [
"Company one" => {
"number_1" => 46,
"number_2" => 3055,
"country" => "USA"
},
"Company two" => {
"number_1" => 32,
"number_2" => 6610,
"country" => "USA"
},
"Company three" => {
"number_1" => 40,
"number_2" => 9128,
"country" => "USA"
}
]
max_company = data.first.max_by { |_, h| h['number_2'] }
p max_company.first # "Company three"
p max_company.last['number_2'] # 9128

Display contents of a hash if value exists

I have a hash:
req = {
"count" => 50100,
"results" => [
{"listing_id" => 615929315, "state" => "active", "user_id" => 140604756, "category_id" => 69150367},
{"listing_id" => 615929311, "state" => "active", "user_id" => 152528025, "category_id" => 69150367}
]
}
I want to find and display the entire internal hash if a particular user_id exists. I can find it:
req["results"][0].select{|key, value| value == 152528025}
# => {"user_id" => 152528025}
How do I then display this entire (nested) hash?
{"listing_id" => 615929311, "state" => "active", "user_id" => 152528025, "category_id" => 69150367}
req["results"].select{|x| x["user_id"] == 152528025}

Order a hash array by an array key

I am trying to filter and reorder an array of hashes. The filter and the order is defined by another array of strings, which represent the value of the "slug" key of the hash. The resulting array should contain only the hashes whose value to the "slug" key is contained in the slugs array and ordered with the same order. If I have the first array as:
data = [
{
"slug" => "lemon",
"label" => "Lemon Label"
},
{
"slug" => "table",
"label" => "Table Label"
},
{
"slug" => "peach",
"label" => "Peach Label"
},
{
"slug" => "strawberry",
"label" => "Strawberry Label"
},
{
"slug" => "bread",
"label" => "Bread Label"
},
{
"slug" => "orange",
"label" => "Orange Label"
}
]
and the second array as:
ordered_keys = ["orange", "lemon", "strawberry"]
then, the result should be an array like this:
result = [
{
"slug" => "orange",
"label" => "Orange Label"
},
{
"slug" => "lemon",
"label" => "Lemon Label"
},
{
"slug" => "strawberry",
"label" => "Strawberry Label"
}
]
I managed to get just the filtering function with this:
result = data.select{|x| ordered_keys.include? x.slug}
but I cannot find a smart way to get the ordered one. Any ideas?
Use map to translate your array of ordered keys into the corresponding hash. The order of the input array to map defines the order of the output array.
ordered_keys.map{|k| data.find{|h| h["slug"] == k}}

ruby one-liner from two hashes

a = {"rows" => [{"id" => "231-z", "name" => 'jon', "age"=> 27, "state" => 'AL'},
{"id" => "4121-x", "name" => 'ton', "age"=> 37, "state" => 'VA'}
]
}
b = {"rows" => [{"key" => ["xyz","4121-x"], "value" =>{"sum" => 12312, "realage" => 29}},
{"key" => ["xyz","231-z"], "value" =>{"sum" => 1212, "realage" => 33}}
]
}
In hash a, age is incorrect
In hash b, realage is correct. Also in hash b id is the second value in the first array that maps to id of hash a . Those are 4121-x, 231-z correspond to hash a
I want to correct the age in hash a and swap it with the realage of hash b
I can do it in multiple steps, but is it possible to do it in one liner or very short? So finally correct hash a should look like
a = {"rows" => [{"id" => "231-z", "name" => 'jon', "age"=> 33, "state" => 'AL'},
{"id" => "4121-x", "name" => 'ton', "age"=> 29, "state" => 'VA'}
]
}
does this look reasonable?
a['rows'].each_with_index do |ah, i|
(bh = b['rows'].select {|h| h['key'].last == ah['id'] }.first) &&
a['rows'][i] = ah.update('age' => bh['value']['realage'])
end
p a
{
"rows" => [
[0] {
"id" => "231-z",
"name" => "jon",
"age" => 33,
"state" => "AL"
},
[1] {
"id" => "4121-x",
"name" => "ton",
"age" => 29,
"state" => "VA"
}
]
}
Please note it will update a only if corresponding id found in b.
Also, the rows order does not matter, nor matter the rows number, it is only important b to have a row with same id as processed row in a
Here is a Working Demo

Group by and format array of hash values in ruby

Hey I have an array of hash values as follows.
[{"group" => "1", "message" => "hey", "weight" => 1}, {"group" => "1", "message"
=> "hey1", "weight" => 2}, {"group" => "2", "message" => "hey3", "weight" => 4}]
I want to group_by group and format it so that I get the following:
[{"group" => 1, "messages" => {"hey","hey1"}, "weights" => {1,2}}, {"group" => 2,
"messages" => {"hey3"}, "weights" => {4}}]
Is there a nice ruby way to achieve this?
Edit: Now I have:
[
{"group" => "1", "message" => {"hey" => "1"}},
{"group" => "1", "message" => {"hey1" => "2"}}
]
I'd like to have
{"group" => "1", "messages" => {"hey1" => "1", "hey2" => "2"} }
Based on your revised question:
groups = [
{"group" => "1", "message" => {"hey" => "1"}},
{"group" => "1", "message" => {"hey1" => "2"}}
]
merged = groups.inject do |h1,h2|
h1.merge(h2) do |k,v1,v2|
if v1==v2
v1
elsif v1.is_a?(Hash) && v2.is_a?(Hash)
v1.merge(v2)
else
[*v1,*v2]
end
end
end
p merged
#=> {"group"=>"1", "message"=>{"hey"=>"1", "hey1"=>"2"}}
I think the output you want is:
[{"messages"=>["hey", "hey1"], "weights"=>[1, 2], "group"=>"1"}, {"messages"=>["hey3"], "weights"=>[4], "group"=>"2"}]
If this is the case, this code does what you want:
h.group_by { |item| item["group"] }.values.map do |item|
item.inject({"messages" => [], "weights" => []}) do |result, subitem|
result["group"] = subitem["group"]
result["messages"] << subitem["message"]
result["weights"] << subitem["weight"]
result
end
end
You should be able to improve it knowing more about your specific problem, but it should be a good starting point.

Resources