Group by and format array of hash values in ruby - 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.

Related

Parsing Hash in Ruby

Bit lost while parsing the hash, actually i want to retrieve the id for "HTTP get traffic" which is in this case 9.? can anyone help?
gettest = {
"Groups" => [
{
"GroupName" => {"TextId" => "Hardware"},
"Tests" => [
{"Description"=>{"TextId"=>"ODU current measurement"}, "Id" => 0}
]
},
{
"GroupName"=>{"TextId"=>"LAN"},
"Tests" => [
{"Description" => {"TextId" => "Ethernet status"}, "Id" => 2},
{"Description" => {"TextId" => "Number of TCP Sessions"}, "Id" => 3}
]
},
{
"GroupName" => {"TextId" => "Satellite connection"},
"Tests" => [
{"Description" => {"TextId" => "Physical layer status"}, "Id" => 4},
{"Description" => {"TextId" => "Data link layer status"}, "Id" => 5},
{"Description" => {"TextId" => "Network layer status"}, "Id" => 6}
]
},
{
"GroupName" => {"TextId" => "Software"},
"Tests" => [
{"Description" => {"TextId" => "Software"}, "Id" => 1}
]
},
{
"GroupName" => {"TextId" => "Traffic"},
"Tests" => [
{"Description" => {"TextId" => "Ping traffic"}, "Id" => 7},
{"Description" => {"TextId" => "DNS traffic"}, "Id" => 8},
{"Description" => {"TextId" => "HTTP get traffic"}, "Id" => 9}
]
}
]
}
anything like this?
traffic_group = gettest['Groups'].find do |group|
group['GroupName']['TextId'] == 'Traffic'
end
if traffic_group
http_get_traffic = traffic_group['Tests'].find do |test|
test['Description']['TextId'] == 'HTTP get traffic'
end
if http_get_traffic
http_get_traffic_id = http_get_traffic['Id']
puts "HTTP get traffic id: #{http_get_traffic_id}"
else
puts 'no http get traffic node found'
end
else
puts 'no traffic group found'
end

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}

Delete an element from a hash based on a condition

I have these parameters:
Parameters: {transactions"=>{
"2"=>{"amount"=>"10", "finance_id"=>"4", "payee_id"=>"5", "category_id"=>"14", "payee_type"=>"Student", "transaction_date"=>"2015-08-10", "title"=>"Receipt No.. (Multiple Fees) F4", "finance_type"=>"FinanceFee", "payment_mode"=>"Cash", "payment_note"=>""},
"1"=>{"amount"=>"10", "finance_id"=>"4", "payee_id"=>"2", "category_id"=>"14", "payee_type"=>"Student", "transaction_date"=>"2015-08-10", "title"=>"Receipt No.. (Multiple Fees) F4", "finance_type"=>"FinanceFee", "payment_mode"=>"Cash", "payment_note"=>""}
}}
I need to set a condition to delete the transaction with amount = 0.I tried this:
params[:transactions].each do |trans|
trans.delete_if {|amount, value| value == 0 || value.nil? || value.empty?}
end
but this doesn't delet the transaction with amount 0.
Just try this
transactions = {
"transactions" =>
{
"2" => {
"amount" => "10",
"finance_id" => "4",
"payee_id" => "5",
"category_id" => "14",
"payee_type" => "Student",
"transaction_date" => "2015-08-10",
"title" => "Receipt No.. (Multiple Fees) F4",
"finance_type" => "FinanceFee",
"payment_mode" => "Cash",
"payment_note" => ""
},
"1" => {
"amount" => "10",
"finance_id" => "4",
"payee_id" => "2",
"category_id" => "14",
"payee_type" => "Student",
"transaction_date" => "2015-08-10",
"title" => "Receipt No.. (Multiple Fees) F4",
"finance_type" => "FinanceFee",
"payment_mode" => "Cash",
"payment_note" => ""
},
"3" => {
"amount" => "0",
"finance_id" => "4",
"payee_id" => "2",
"category_id" => "14",
"payee_type" => "Student",
"transaction_date" => "2015-08-10",
"title" => "Receipt No.. (Multiple Fees) F4",
"finance_type" => "FinanceFee",
"payment_mode" => "Cash",
"payment_note" => ""
},
"4" => {
"amount" => nil,
"finance_id" => "4",
"payee_id" => "2",
"category_id" => "14",
"payee_type" => "Student",
"transaction_date" => "2015-08-10",
"title" => "Receipt No.. (Multiple Fees) F4",
"finance_type" => "FinanceFee",
"payment_mode" => "Cash",
"payment_note" => ""
},
"4" => {
"amount" => "",
"finance_id" => "4",
"payee_id" => "2",
"category_id" => "14",
"payee_type" => "Student",
"transaction_date" => "2015-08-10",
"title" => "Receipt No.. (Multiple Fees) F4",
"finance_type" => "FinanceFee",
"payment_mode" => "Cash",
"payment_note" => ""
}
}
}
transactions["transactions"].delete_if{|_, v| v["amount"].to_s.strip.to_i == 0}
Your value is an string convert this to integer
value.to_i == 0
or
value == 0.to_s
or
value.to_i.zero?

Nested hash iteration: How to iterate a merge over an ( (array of hashes) within a hash )

I'm trying to do as the title says. Here is my code:
school.each { |x| school[:students][x].merge!(semester:"Summer") }
I think I pinpointed the problem to the "[x]" above. If I substitute an array position such as "[2]" it works fine. How can make the iteration work?
If the info above is not enough or you'd like to offer a better solution, please see the details below. Thanks!
The error message I get:
file.rb:31:in []': no implicit conversion of Array into Integer (TypeError)
from file.rb:31:inblock in '
from file.rb:31:in each'
from file.rb:31:in'
The nested hash below before alteration:
school = {
:name => "Happy Funtime School",
:location => "NYC",
:instructors => [
{:name=>"Blake", :subject=>"being awesome" },
{:name=>"Ashley", :subject=>"being better than blake"},
{:name=>"Jeff", :subject=>"karaoke"}
],
:students => [
{:name => "Marissa", :grade => "B"},
{:name=>"Billy", :grade => "F"},
{:name => "Frank", :grade => "A"},
{:name => "Sophie", :grade => "C"}
]
}
I'm trying to append :semester=>"Summer" to each of the last four hashes. Here is what I'm trying to go for:
# ...preceding code is the same. Changed code below...
:students => [
{:name => "Marissa", :grade => "B", :semester => "Summer"},
{:name=>"Billy", :grade => "F", :semester => "Summer"},
{:name => "Frank", :grade => "A", :semester => "Summer"},
{:name => "Sophie", :grade => "C", :semester => "Summer"}
]
}
Just iterate over the students:
school[:students].each { |student| student[:semester] = "Summer" }
Or, using merge:
school[:students].each { |student| student.merge!(semester: "Summer") }
The issue is that when you do array.each {|x| do something}, x actually refers to each element in the array.
For example, in the first iteration of the loop,
x = {:name => "Marissa", :grade => "B"}
So what you are really doing is trying to reference:
school[:student][{:name => "Marissa", :grade => "B"}]
Which will not work
What you could do instead is create a for loop to track the index.
for i in 0 ... school[:student].count
school[:students][i].merge!(semester:"Summer")
end
Edit: Stefan's solution is much better than mine, but I will leave this up to show where you went wrong.
I would do as below using Hash#store :
require 'awesome_print'
school = {
:name => "Happy Funtime School",
:location => "NYC",
:instructors => [
{
:name => "Blake",
:subject => "being awesome"
},
{
:name => "Ashley",
:subject => "being better than blake"
},
{
:name => "Jeff",
:subject => "karaoke"
}
],
:students => [
{
:name => "Marissa",
:grade => "B"
},
{
:name => "Billy",
:grade => "F"
},
{
:name => "Frank",
:grade => "A"
},
{
:name => "Sophie",
:grade => "C"
}
]
}
school[:students].each{|h| h.store(:semester ,"Summer")}
ap school,:index => false,:indent => 10
output
{
:name => "Happy Funtime School",
:location => "NYC",
:instructors => [
{
:name => "Blake",
:subject => "being awesome"
},
{
:name => "Ashley",
:subject => "being better than blake"
},
{
:name => "Jeff",
:subject => "karaoke"
}
],
:students => [
{
:name => "Marissa",
:grade => "B",
:semester => "Summer"
},
{
:name => "Billy",
:grade => "F",
:semester => "Summer"
},
{
:name => "Frank",
:grade => "A",
:semester => "Summer"
},
{
:name => "Sophie",
:grade => "C",
:semester => "Summer"
}
]
}

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

Resources