简体   繁体   中英

How to nest an array of hashes with group by as json?

I have data that looks like this:

[
  {date: '2014/10/01', group: 'a', node: '2'},
  {date: '2014/10/01', group: 'b', node: '3'},
  {date: '2014/10/02', group: 'a', node: '4'},
  {date: '2014/10/02', group: 'b', node: '1'}
]

How would I go about first grouping by date like so:

.group_by{|x| [x.date.strftime("%Y-%m-%d")}

but then by group and then node and render that as a nested json?

I'm looking for something like this:

[
  {'2014/10/01': {groups: [{a: ['2'], b: ['3']}]},
  {'2014/10/02': {groups: [{a: ['4'], b: ['1']}]},
]

Try this:

arr = [
  {date: '2014/10/01', group: 'a', node: '2'},
  {date: '2014/10/01', group: 'b', node: '3'},
  {date: '2014/10/02', group: 'a', node: '4'},
  {date: '2014/10/02', group: 'b', node: '1'}
]

arr.group_by { |item| item[:date] }.map do |k,v|
  Hash[k, { groups: v.map { |g| Hash[g[:group], [g[:node]]] }}]
end
#  => [{"2014/10/01"=>{:groups=>[{"a"=>["2"]}, {"b"=>["3"]}]}}, {"2014/10/02"=>{:groups=>[{"a"=>["4"]}, {"b"=>["1"]}]}}] 

When faced with problems like this one, involving multiple transformations to the data, I often find it useful to divide the problem into two steps, the first being to create an array or hash that contains all the essential information; the second being to format that data in the form desired.

It's best to describe the procedure around an example. The one given in the question is fine:

arr = [
  {date: '2014/10/01', group: 'a', node: '2'},
  {date: '2014/10/01', group: 'b', node: '3'},
  {date: '2014/10/02', group: 'a', node: '4'},
  {date: '2014/10/02', group: 'b', node: '1'}
]

As we will be grouping on the date, it makes sense to extract the data from arr into a hash whose keys are dates. The values of those keys are hashes that contain key-value pairs corresponding to the values of :group and :node in each element of arr . I've chosen to do that using the form of Hash#update (aka merge! ) that uses a block to determine the values of keys that are present in both hashes being merged:

h = arr.each_with_object({}) { |g,h|
  h.update(g[:date]=>{ g[:group]=>g[:node] }) { |_,oh,nh|oh.update(nh) } }
  #=> {"2014/10/01"=>{"a"=>"2", "b"=>"3"},
  #    "2014/10/02"=>{"a"=>"4", "b"=>"1"}}

Before moving on to the formatting step, I should point out that, depending on requirements, it may more convenient to use this hash in subsequent calculations, rather than the array requested.

Formatting is now straightforward, using Enumerable#map :

h.map { |d,g| { d=>{groups: [g] } } }
  #=> [{"2014/10/01"=>{:groups=>[{"a"=>"2", "b"=>"3"}]}},
  #    {"2014/10/02"=>{:groups=>[{"a"=>"4", "b"=>"1"}]}}] 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM