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.
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"]
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.
Related
I need delete all key created_at and updated_at in array of hashs . My hash looks like :
assessment_with_desc = {
"id": 1,
"name": "First assessment",
"created_at": "2020-03-14T20:13:27.006Z",
"updated_at": "2020-03-14T20:13:27.006Z",
"description_with_child_models": [
{
"id": 3,
"title": "First category",
"created_at": "2020-02-20T15:32:46.379Z",
"updated_at": "2020-03-14T20:16:11.530Z",
"accessment_id": 1,
"sub_categories": [
{
"id": 1,
"title": "First sub_category",
"category_id": 3,
"created_at": "2020-02-20T15:40:49.793Z",
"updated_at": "2020-02-20T15:40:49.793Z",
"stages": [
{
"id": 5,
"title": "First stage",
"sub_category_id": 1,
"created_at": "2020-02-20T15:44:10.603Z",
"updated_at": "2020-02-20T15:44:10.603Z"
}
]
}
]
}
]
}
I did it, but it work only in this case assessment_with_desc.delete('created_at') and assessment_with_desc.delete('updated_at'):
assessment_with_desc.delete('created_at')
assessment_with_desc.delete('updated_at')
assessment_with_desc['description_with_child_models'].delete('created_at')
assessment_with_desc['description_with_child_models'].delete('updated_at')
assessment_with_desc['description_with_child_models'][0]['sub_categories'].delete('created_at')
assessment_with_desc['description_with_child_models'][0]['sub_categories'].delete('updated_at')
assessment_with_desc['description_with_child_models'][0]['sub_categories'][0]['stages'].delete('created_at')
assessment_with_desc['description_with_child_models'][0]['sub_categories'][0]['stages'].delete('updated_at')
You can recursively invoke a function to remove the key/value in case the key is equal to :created_at or :updated_at:
def recursively_delete_timestamps(object)
object.transform_values do |value|
next value unless value.is_a?(Array)
value.map do |inner_hash|
recursively_delete_timestamps(inner_hash)
end
end.reject do |key, _|
key.in?(%i[created_at updated_at])
end
end
recursively_delete_timestamps(assessment_with_desc)
# {:id=>1,
# :name=>"First assessment",
# :description_with_child_models=>
# [{:id=>3,
# :title=>"First category",
# :accessment_id=>1,
# :sub_categories=>
# [{:id=>1,
# :title=>"First sub_category",
# :category_id=>3,
# :stages=>[{:id=>5, :title=>"First stage", :sub_category_id=>1}]}]}]}
Notice, the hash remains unchanged. Nor transform_values, map or reject modify the original object, but return a new one.
In my ruby on rails application am getting below data from API service, the data format is array of hashes like below.
data = [
{"category": "Population.Behaviors.Commute", "tag": "away", "description": "Work Outside the Home"},
{"category": "Population.Behaviors.Commute.Vehicle", "tag": "mbike", "description": "Bike to Work"}
]
The above code format I have to convert to the below format for generating the form elements.
response_format = [
{
"label": "Population",
"options": [
{
"label": "Behaviors",
"options": [
{
"label": "Commute",
"options": [
{
"label": "Vehicle",
"options": [
{
"tag": "mbike",
"description": "Bike to Work"
}
]
},
{
"tag": "away",
"description": "Work Outside the Home"
}
]
}
]
}
]
}
]
Anyone kindly help to achieve the solution.
All you need is to recursively build an inner hash:
data.
each_with_object(Hash.new { |h, k| h[k] = h.dup.clear }) do |h, acc|
(h[:category].split('.').
reduce(acc) do |inner, cat|
inner["label"] = cat
inner["options"] ||= {}
end || {}).
merge!("tag" => h[:tag], "description" => h[:description])
end
#⇒ {
# "label" => "Population",
# "options" => {
# "label" => "Behaviors",
# "options" => {
# "label" => "Commute",
# "options" => {
# "description" => "Work Outside the Home",
# "label" => "Vehicle",
# "options" => {
# "description" => "Bike to Work",
# "tag" => "mbike"
# },
# "tag" => "away"
# }
# }
# }
# }
I am trying to load data from redis db. I have a api only rails app and trying to render the json data as per requirement.
Currently I am able to get the data from redis in the following format.
[
{
"id": 1,
"name": "Stephenie Meyer",
"created_at": "2018-04-17T07:40:50.417Z",
"updated_at": "2018-04-17T07:40:50.417Z"
},
{
"id": 2,
"name": "V.C. Andrews",
"created_at": "2018-04-17T07:40:50.613Z",
"updated_at": "2018-04-17T07:40:50.613Z"
},
{
"id": 3,
"name": "Sophie Kinsella",
"created_at": "2018-04-17T07:40:50.646Z",
"updated_at": "2018-04-17T07:40:50.646Z"
}
]
How can convert this in a way such that the key value pairs of name,created and updated will be hash to a id key-value pair.
Into this
{"id": 1,
{
"name": "Stephenie Meyer",
"created_at": "2018-04-17T07:40:50.417Z",
"updated_at": "2018-04-17T07:40:50.417Z"
}
}
helper method for getting redis data.
def fetch_authors
authors = $redis.get('authors')
if authors.nil?
authors = Author.all.to_json
$redis.set("authors", authors).to_json
$redis.expire("authors", 5.hour.to_i)
end
JSON.load authors
end
And displaying on index page using
def index
#authors = fetch_authors
render json: #authors
end
The closest to what you want would probably be:
input = ...
input.map { |hash| [hash.delete(:id) || hash.delete('id'), hash] }.to_h
#⇒ {{1=>{:name=>...},
# {2=>{:name=>...},
# {3=>{:name=>...}}
Not exactly what you want because that's not correct syntax but you can achieve something similar with group_by
arr = [
{
"id": 1,
"name": "Stephenie Meyer",
"created_at": "2018-04-17T07:40:50.417Z",
"updated_at": "2018-04-17T07:40:50.417Z"
},
{
"id": 2,
"name": "V.C. Andrews",
"created_at": "2018-04-17T07:40:50.613Z",
"updated_at": "2018-04-17T07:40:50.613Z"
},
{
"id": 3,
"name": "Sophie Kinsella",
"created_at": "2018-04-17T07:40:50.646Z",
"updated_at": "2018-04-17T07:40:50.646Z"
}
]
arr.group_by { |e| e[:id] }
This will return
{
1 => [
{
:id => 1,
:name => "Stephenie Meyer",
:created_at => "2018-04-17T07:40:50.417Z",
:updated_at => "2018-04-17T07:40:50.417Z"
}
],
2 => [
{
:id => 2,
:name => "V.C. Andrews",
:created_at => "2018-04-17T07:40:50.613Z",
:updated_at => "2018-04-17T07:40:50.613Z"
}
],
3 => [
{
:id => 3,
:name => "Sophie Kinsella",
:created_at => "2018-04-17T07:40:50.646Z",
:updated_at => "2018-04-17T07:40:50.646Z"
}
]
}
Hi guys I am having trouble to find out a solution to a problem.So I have an array like bellow.
{
"9": [
{
"id": "9",
"day": "2017-08-02",
"voltage": "3397.1"
},
{
"id": "9",
"day": "2017-08-01",
"voltage": "11518.67"
},
{
"id": "9",
"day": "2017-07-31",
"voltage": "12835.5900000002"
}
],
"11": [
{
"id": "11",
"day": "2017-08-02",
"voltage": "910.21"
},
{
"id": "11",
"day": "2017-08-01",
"voltage": "3616.43"
},
{
"id": "11",
"day": "2017-07-31",
"voltage": "2085.37"
}
],
"12": [
{
"id": "12",
"day": "2017-08-02",
"voltage": "4793.96"
},
{
"id": "12",
"day": "2017-08-01",
"voltage": "17762.2999999998"
},
{
"id": "12",
"day": "2017-07-31",
"voltage": "18334.4000000001"
}
]
}
Now what I want to do is to produce an array like below
{
"9": [
"day": ["2017-08-02", "2017-08-01", "2017-07-31"],
"voltage": ["3397.1", "11518.67", "12835.5900000002"]
],
"11": [
"day": ["2017-08-02","2017-08-01", "2017-07-31"]
"voltage": ["910.21", "3616.43", "2085.37"]
]
...
}
and so on.
I have tried to iterate over the array using .each and .map method but it didn't work.I search trough existing solutions in stackoverflow but nothing helps me out.
Can some one help me with this.
Thanks so much in advance.
If you define hash as the original object you can do it with reduce like so:
result = hash.reduce({}) do |memo, (key, vals)|
memo[key] = {
"day" => vals.map { |val| val[:day] },
"voltage" => vals.map { |val| val[:voltage] }
}
memo
end
You can also do it with just each:
result = {}
hash.each do |key, vals|
result[key] = {
"day" => vals.map { |val| val[:day] },
"voltage" => vals.map { |val| val[:voltage] }
}
end
I am unsure if you are writing your hash as JSON or Ruby, but I didn't have time to rewrite it to Ruby, to I just copy pasted it into a script, which made all the keys be symbols.
I believe this should be working:
new_hash = {}
current_hash.each do |key, value_array|
new_hash[key] ||= {}
value_array.each do |values|
new_values = values.dup
new_values.delete(:id)
new_values.each do |attribute_name, attribute_value|
new_hash[key][attribute_name] ||= []
new_hash[key][attribute_name] << attribute_value
end
end
end
Which gives me this result:
{:"9"=>{:day=>["2017-08-02", "2017-08-01", "2017-07-31"], :voltage=>["3397.1", "11518.67", "12835.5900000002"]}, :"11"=>{:day=>["2017-08-02", "2017-08-01", "2017-07-31"], :voltage=>["910.21", "3616.43", "2085.37"]}, :"12"=>{:day=>["2017-08-02", "2017-08-01", "2017-07-31"], :voltage=>["4793.96", "17762.2999999998", "18334.4000000001"]}}
One more solution from me :)
For Ruby 2.4.0:
hash.transform_values do |values|
values.each_with_object({}) do |value, memo|
memo[:day] ? memo[:day] << value[:day] : memo[:day] = [value[:day]]
memo[:voltage] ? memo[:voltage] << value[:voltage] : memo[:voltage] = [value[:voltage]]
end
end
And for earlier:
hash.map do |key, values|
[
key,
values.each_with_object({}) do |value, memo|
memo[:day] ? memo[:day] << value[:day] : memo[:day] = [value[:day]]
memo[:voltage] ? memo[:voltage] << value[:voltage] : memo[:voltage] = [value[:voltage]]
end
]
end.to_h
array = [
[ 1, "name1" ],
[ 2, "name2" ],
[ 3, "name3" ],
[ 4, "name4" ]
]
I want to make this as an array of hashes like this:
array_hash = [{ "id" => 1, "name" => "name1" },
{ "id" => 2, "name" => "name2" },
{ "id" => 3, "name" => "name3" },
{ "id" => 4, "name" => "name4" }]
array = [
[ 1, "name1" ],
[ 2, "name2" ],
[ 3, "name3" ],
[ 4, "name4" ]
]
array.map { |e| ['id', 'name'].zip(e).to_h }
#⇒ [
# {"id"=>1, "name"=>"name1"},
# {"id"=>2, "name"=>"name2"},
# {"id"=>3, "name"=>"name3"},
# {"id"=>4, "name"=>"name4"}
# ]
The only interesting here is Enumerable#zip, that “merges” arrays.
I'd use:
array.map { |id, name| { 'id' => id, 'name' => name } }
#=> [{"id"=>1, "name"=>"name1"},
# {"id"=>2, "name"=>"name2"},
# {"id"=>3, "name"=>"name3"},
# {"id"=>4, "name"=>"name4"}]
The .to_h method is new to Ruby 2.x. Here is an alternative for anyone on 1.9.x or lower.
array = [[ 1, "name1" ], [ 2, "name2" ], [ 3, "name3" ], [ 4, "name4" ]]
array.inject([]) { |a, r| a << { id: r[0], name: r[1] } }