Rails iterate multiple hash - ruby-on-rails

I have the following hash :
{
"2017-01-01" => {
"2"=> [
{:a=>"2017-01-01", :b=>"2", :c=>"1"},
{:a=>"2017-01-01", :b=>"2", :c=>"2"}
]
},
"2017-01-02" => {
"5"=> [
{:a=>"2017-01-02", :b=>"5", :c=>"1"}
]
}
}
I would iterate separately
1)first iteration
{
{:a=>"2017-01-01", :b=>"2", :c=>"1"},
{:a=>"2017-01-01", :b=>"2", :c=>"2"}
}
2) second iteration
{
{:a=>"2017-01-02", :b=>"5", :c=>"1"}
}
How can I do? Thanks in advance.

answer for your question is in How to iterate over a hash in Ruby?
check it.
hash.each do |key, array|
puts array
end
if 'array' again is a hash, then you need to loop it as follows
hash.each do |key, hash2|
hash2.each do |key2,array|
puts array
end
end

Related

How to search nested hash of arrays and arrays of hash and return multiple matching objects from the parent node?

Say I have the below ruby hash nested
hash_or_array = [{
"book1" => "buyer1",
"book2" => {
"book21" => "buyer21", "book22" => ["buyer23", "buyer24", true]
},
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
},
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"],
"book5" => {
"book5,1" => "buyer5"
}
}]
And I want to look for the string buyer35. Upon match, it should return the following
[
{
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
The solution below (from another SO question, link below), returns the first match, but I am trying to figure out how to return multiple matches
def recurse(obj, target)
case obj
when Array
obj.each do |e|
case e
when Array, Hash
rv = recurse(e, target)
return [rv] unless rv.nil?
when target
return e
end
end
when Hash
obj.each do |k,v|
case v
when Array, Hash
rv = recurse(v, target)
return {k=>rv} unless rv.nil?
when target
return {k=>v}
end
end
end
nil
end
This is the original question and answer: How to search nested hash of arrays and arrays of hash and only return matching object from the parent node?
UPDATE: The correct return format should be
[
{
"book3" => {
"3" => [{
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
Here is a function that recursively searches for the target value in any nested arrays or hashes. The function is then used to select the entries in your top level hash_or_array for entries that contain the target and adds them to an array.
require 'pp'
def find_value(obj, target, found = false)
found = true if obj == target
return found unless obj.is_a?(Array) || obj.is_a?(Hash)
case obj
when Hash
obj.each { |k, v| found = find_value(v, target, found) }
when Array
obj.each { |v| found = find_value(v, target, found) }
end
found
end
found_entries = hash_or_array.inject([]) {|entries, obj| entries << obj.select { |k, v| find_value({ k => v }, "buyer35") }}
pp found_entries
=>
[{"book3"=>
{"0"=>"buyer31",
"1"=>"buyer32",
"2"=>"buyer33",
"3"=>[{"4"=>"buyer34", "5"=>[10, 11], "6"=>[{"7"=>"buyer35"}]}]},
"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
Here is a recursive solution to your "real" question. Since it mutates the original object, I used a "trick" to make a deep copy first. The keep_entries_with produces an object with the original shape as your input, but since your output shape is different the second step just transforms the filtered result into the shape of your desired output.
deep_copy = Marshal.load(Marshal.dump(hash_or_array))
def keep_entries_with(target, obj)
return unless obj.is_a?(Hash) || obj.is_a?(Array)
case obj
when Hash
keep_entries_with(target, obj.values)
obj.keep_if do |k, v|
v == target ||
v.is_a?(Hash) && v.values.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) } ||
v.is_a?(Array) && v.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) }
end
when Array
obj.each do |v|
keep_entries_with(target, v)
end
end
end
filtered = keep_entries_with("buyer35", deep_copy)
final_result = filtered.first.map { |k, v| { k => v } }
pp final_result
which produces:
[{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}},
{"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
The code below appears to generate the desired output for this specific case:
hash_or_array.inject([]) do |result, x|
x.keep_if { |k, v| v.to_s =~ /buyer35/ }
result << x
end

how to create an array of hashes by looping over array of objects

I have following array of hash. I am trying to loop over it and build an array of hash of values of id and product_order_id.
objects =
[
#<Product: 0x00007ffd4a561108
#id="1",
#product_id="2",
#product_order_id="23",
#description="abc",
#status="abcde",
#start_date=nil,
#end_date=nil>,
#<Product: 0x00007ffd4a560c80
#id="45",
#product_id="22",
#product_order_id="87",
#description="ahef",
#status="gesff",
#start_date=nil,
#end_date=nil>
......more objects.....
]
This is what it should look like
[{ "1": "23" }, { "45": "87" }] -->its going to be uuid
I tried doing this but no luck
def mapped_product(objects)
mapping = []
objects.each do |object|
mapping << {
object.product_order_id: object.id
}
end
end
Any idea?
inline solution:
> Hash[objects.map{|p| [p.id, p.product_order_id] }]
# Output : [{ 1=>23 }, { 45=>87 }]
I'd usually implement it using an each_with_object
objects.each_with_object({}) { |obj, acc| acc[obj.id] = obj.product_order_id }
Unless I reaaaly want to squeeze some performance, than I'd go with Gagan's answer
Have you tried this?
def mapped_product(objects)
mapping = []
objects.each do |object|
mapping << {
object.id => object.product_order_id # I'm using an `=>` here
}
end
mapping # return the new mapping
end
I've just changed the : on the hash for a => to "make it dynamic" and swapped the values of id and product_order_id
You can also use a map here:
def mapped_product(objects)
objects.map do |object|
{ object.id => object.product_order_id }
end
end

Group Hash by values in ruby

I have a hash in ruby which looks something like this:
{
"admin_milestones"=>"1",
"users_milestones"=>"0",
"admin_goals"=>"1",
"users_goals"=>"0",
"admin_tasks"=>"1",
"users_tasks"=>"0",
"admin_messages"=>"1",
"users_messages"=>"0",
"admin_meetings"=>"1",
"users_meetings"=>"0"
}
I am trying to lookout for a solutions which can cut this hash in to two parts, one with value as 1 and other hash with value as 0.
You can group hash by its value:
h1 = {
"admin_milestones"=>"1",
"users_milestones"=>"0",
"admin_goals"=>"1",
"users_goals"=>"0",
"admin_tasks"=>"1",
"users_tasks"=>"0",
"admin_messages"=>"1",
"users_messages"=>"0",
"admin_meetings"=>"1",
"users_meetings"=>"0"
}
h2 = h1.group_by{|k,v| v}
It will produce a hash grouped by its values like this:
h2 = {"1"=>[["admin_milestones", "1"], ["admin_goals", "1"], ["admin_tasks", "1"], ["admin_messages", "1"], ["admin_meetings", "1"]],
"0"=>[["users_milestones", "0"], ["users_goals", "0"], ["users_tasks", "0"], ["users_messages", "0"], ["users_meetings", "0"]]}
If you want an array as answer the cleanest solution is the partition method.
zeros, ones = my_hash.partition{|key, val| val == '0'}
You should use group_by on the keys arrays and use the value as the grouping element:
h1 = {
"admin_milestones"=>"1",
"users_milestones"=>"0",
"admin_goals"=>"1",
"users_goals"=>"0",
"admin_tasks"=>"1",
"users_tasks"=>"0",
"admin_messages"=>"1",
"users_messages"=>"0",
"admin_meetings"=>"1",
"users_meetings"=>"0"
}
# group_by on the keys, then use the value from the hash as bucket
h2 = h1.keys.group_by { |k| h1[k] }
puts h2.inspect
Returns a hash from value to array of keys:
{
"1" => [
[0] "admin_milestones",
[1] "admin_goals",
[2] "admin_tasks",
[3] "admin_messages",
[4] "admin_meetings"
],
"0" => [
[0] "users_milestones",
[1] "users_goals",
[2] "users_tasks",
[3] "users_messages",
[4] "users_meetings"
]
}
Just Hash.select:
h1.select { |key, value| value == '0' } #=> {"users_milestones"=>"0", "users_goals"=>"0", ...}
h1.select { |key, value| value == '1' } #=> {"admin_milestones"=>"1", "admin_goals"=>"1", ...}
The return value depends on your Ruby version. Ruby 1.8 returns a array of arrays, whereas Ruby 1.9 returns a hash like in the example above.
Similar with https://stackoverflow.com/a/56164608/14718545 you can use group_by but with then, in this case, you will avoid instantiating an extra variable.
{
"admin_milestones" => "1",
"users_milestones" => "0",
"admin_goals" => "1",
"users_goals" => "0",
"admin_tasks" => "1",
"users_tasks" => "0",
"admin_messages" => "1",
"users_messages" => "0",
"admin_meetings" => "1",
"users_meetings" => "0"
}.then { |h| h.keys.group_by { |k| h[k] } }
{"1"=>["admin_milestones", "admin_goals", "admin_tasks", "admin_messages", "admin_meetings"],
"0"=>["users_milestones", "users_goals", "users_tasks", "users_messages", "users_meetings"]}

JBuilder loop that produces hash

I need loop that produces hash, not an array of objects. I have this:
json.service_issues #service.issues do |issue|
json.set! issue.id, issue.name
end
that results:
service_issues: [
{
3: "Not delivered"
},
{
6: "Broken item"
},
{
1: "Bad color"
},
{
41: "Delivery problem"
}
]
I need this:
service_issues: {
3: "Not delivered",
6: "Broken item",
1: "Bad color",
41: "Delivery problem"
}
Is it possible to do this without converting AR result to hash manually?
Jbuilder dev here.
Short answer: Yes. It's possible without converting array of models into hash.
json.service_issues do
#service.issues.each{ |issue| json.set! issue.id, issue.name }
end
but it'd probably be easier to prepare hash before-hand.
json.service_issues Hash[#service.issues.map{ |issue| [ issue.id, issue.name ] }]
For anyone who is interested in having an hash of arrays (objects), you can use the following code:
#bacon_types.each do |bacon_type|
json.set! bacon_type.name, bacon_type.bacons do |bacon|
bacon.title bacon.title
...
end
You can do it like this way
Jbuilder.encode do |json|
json.service_issues #service.issues.inject({}) { |hash, issue| hash[issue.id] = issue.name; hash }
end
The code generating hash technique may be understood by following example.
[1] pry(main)> array = [{id: 1, content: 'a'}, {id: 2, content: 'b'}]
=> [{:id=>1, :content=>"a"}, {:id=>2, :content=>"b"}]
[2] pry(main)> array.inject({}) { |hash, element| hash[element[:id]] = element[:content]; hash }
=> {1=>"a", 2=>"b"}
The key point of inject to generate hash, return created hash every after inserting new element. Above example, it is realized by ; hash.

how can I simply merge a hash into a new one?

I have a simple hash like so { "1234" => "5", "2345" => "6" }
How can I create a new hash with both the keys and values in side it? Like so:
{ key_id = "1234", value_id = "5" }, { key_id = "2345", value_id = "6" }
What are you actually trying to achieve with this? If you're looking to iterate over all of the keys, you can use .keys:
h = { "1234" => "5", "2345" => "6" }
h.keys
=> ["1234", "2345"]
If you want to just create an array of hashes, you should be able to iterate over the keys:
h = { "1234" => "5", "2345" => "6" }
a = []
h.each {|k, v| a << {:key_id => k, :value_id => v}
By "merging" two hashes, I think you mean to put all the contents of two different hashes into one new hash. Because the keys of a hash must be unique, if the same key exists in the both source hashes, only one value can survive.
In this example, I merge the contents of hash x and hash y into hash z. The values in y will overwrite the values in z if there are any duplicate keys.
x = { "a" => "1","b" => "2","c" => "3" }
y = { "c" => "999","d" => "4","e" => "5" }
z = {}
x.each do |key,value|
z[key] = value
end
y.each do |key,value|
z[key] = value
end
The source hashes had a total of 6 keys. Because the key "c" was in both hases, the merged hash only has 5 keys.
=> {"a"=>"1", "b"=>"2", "c"=>"999", "d"=>"4", "e"=>"5"}
You can loop through each pair of the original hash and build up an array of hashes:
hashes = []
{ "1234" => "5", "2345" => "6" }.each_pair {|key, value| hashes << { :key_id => key, :value_id => value } }
Will yield:
[{:key_id=>"2345", :value_id=>"6"}, {:key_id=>"1234", :value_id=>"5"}]
What should the keys be for the derived hash, the same at the original? In that case use this snippet:
x = { "1234" => "5", "2345" => "6" }
y = {}
x.each do |key, value|
y[key] = { "key_id" => key, "value_id" => value }
end

Resources