Reputation: 69
I have the following json,
{
"items": [
{
"name": "table",
"item_group": [
{
"code": "code1",
"section": [
{
"unit": "centimeter",
"values": [
{
"display": "151.13 centimeter"
}
]
},
{
"unit": "centimeter (qualifier value)",
"values": [
{
"display": "170.39 centimeter"
}
]
}
],
"total": 2
}
],
"more_results": false,
"total": 1
}
]
}
How do I iterate through this and create an array of "values". I want something like [151.13 centimeter, 170.39 centimeter,....]. Please show some direction in this. thanks.
Note: I have to do this in Haml.
Upvotes: 2
Views: 183
Reputation: 110685
Assuming you know the structure of the underlying hash, you can do this:
Code
require 'json'
JSON.parse(json_str)['items'].each_with_object([]) do |g,arr|
g['item_group'].each do |gg|
gg['section'].each do |ggg|
ggg['values'].each { |gggg|arr << gggg['display'][/\d+\.\d+/].to_f }
end
end
end
#=> [151.13, 170.39]
Explanation
The steps are as follows:
a = JSON.parse(json_str)
b = a['items']
enum = b.each_with_object([])
#=> #<Enumerator: [{"name"=>"table",..."total"=>1}]:each_with_object([])>
We can see the elements of the enumerator by converting it to an array:
enum.to_a
#=> [[{"name"=>"table", "item_group"=>[{"code"=>"code1",
# "section"=>[{"unit"=>"centimeter",
# "values"=>[{"display"=>"151.13 centimeter"}]},
# { "unit"=>"centimeter (qualifier value)",
# "values"=>[{"display"=>"170.39 centimeter"}]}], "total"=>2}],
# "more_results"=>false,
# "total"=>1},
# []]]
Notice that this array has a single element, a two-element array containing a hash and an empty array.
The first element of enum
is passed to the block and assigned to the block variables using parallel assignment:
g, arr = enum.next
#=> {"name"=>"table",
# "item_group"=>[
# {"code"=>"code1",
# "section"=>[
# {"unit"=>"centimeter",
# "values"=>[
# {"display"=>"151.13 centimeter"}
# ]
# },
# {"unit"=>"centimeter (qualifier value)",
# "values"=>[
# {"display"=>"170.39 centimeter"}
# ]
# }
# ],
# "total"=>2}],
# "more_results"=>false,
# "total"=>1}
arr #=> []
c = g['item_group']
#=> [{"code"=>"code1",
# "section"=>[
# {"unit"=>"centimeter",
# "values"=>[
# {"display"=>"151.13 centimeter"}
# ]
# },
# {"unit"=>"centimeter (qualifier value)",
# "values"=>[
# {"display"=>"170.39 centimeter"}
# ]
# }
# ],
# "total"=>2}]
Note c
has the form [hash]
.
The first (and only) element of c
is passed to its block and assigned to its block variable:
gg = c.first
d = gg['section']
#=> [{"unit"=>"centimeter",
"values"=>[
{"display"=>"151.13 centimeter"}
]
},
{"unit"=>"centimeter (qualifier value)",
"values"=>[
{"display"=>"170.39 centimeter"}
]
}
]
ggg = d.first
#=> {"unit"=>"centimeter",
# "values"=>[
# {"display"=>"151.13 centimeter"}
# ]
# }
e = ggg['values']
#=> [{"display"=>"151.13 centimeter"}]
gggg = e.first
#=> {"display"=>"151.13 centimeter"}
f = gggg['display']
#=> "151.13 centimeter"
g = f[/\d+\.\d+/]
#=> "151.13"
i = g.to_f
#=> 151.13
arr << i
#=> [151.13]
arr
#=> [151.13]
The remaining calculations are similar.
Alternative expression
If, as in the example, the arrays JSON.parse(json_str)['items']
, g['item_group']
and ggg['values']
each contain a single element (a hash), you could instead write:
JSON.parse(json_str)['items'].first['item_group'].first['section'].
each_with_object([]) do |g, arr|
arr << g['values'].first['display'][/\d+\.\d+/].to_f
end
#=> [151.13, 170.39]
though I doubt that would be significantly more efficient.
Upvotes: 1
Reputation: 2672
I know there are multiple ways of doing this. But if you really have many complex JSON queries, you can try ruby-jq gem. It is very fast, as it uses linux pipes
require 'jq'
require 'jq/extend'
json_content = {
"items": [
{
"name": "table",
"item_group": [
{
"code": "code1",
"section": [
{
"unit": "centimeter",
"values": [
{
"display": "151.13 centimeter"
}
]
},
{
"unit": "centimeter (qualifier value)",
"values": [
{
"display": "170.39 centimeter"
}
]
}
],
"total": 2
}
],
"more_results": false,
"total": 1
}
]
}
jq_filter = ' .items | .[].item_group |.[].section| . [ ] .values | . [ ] .display'
final_array = json_content.jq(jq_filter)
# => final_array = ["151.13 centimeter", "170.39 centimeter"]
Upvotes: 1