简体   繁体   中英

How do I group objects into nested objects by a value in the object?

Here's an object I'm working with in Ruby. It's a bunch of events sorted by date, but I want to nest the object one level deeper when there are two or more with the same event_type next to each other in the object.

@events = [ 
    { :id => 1, :event_type => "B", :created_at => "2013-09-30 14:44:24 UTC"},
    { :id => 2, :event_type => "A", :created_at => "2013-09-29 14:44:24 UTC"},
    { :id => 3, :event_type => "A", :created_at => "2013-09-28 14:44:24 UTC"},
    { :id => 4, :event_type => "C", :created_at => "2013-09-27 14:44:24 UTC"},
    { :id => 5, :event_type => "C", :created_at => "2013-09-26 14:44:24 UTC"},
    { :id => 6, :event_type => "A", :created_at => "2013-09-25 14:44:24 UTC"}
]

I'm looking to turn it into this:

@events = [
    { :id => 1, :event_type => "B", :created_at => "2013-09-30 14:44:24 UTC"},
    { :grouped_events => [
            { :id => 2, :event_type => "A", :created_at => "2013-09-29 14:44:24 UTC"},
                { :id => 3, :event_type => "A", :created_at => "2013-09-28 14:44:24 UTC"}
            ]
        },
    { :grouped_events => [
            { :id => 4, :event_type => "C", :created_at => "2013-09-27 14:44:24 UTC"},
            { :id => 5, :event_type => "C", :created_at => "2013-09-26 14:44:24 UTC"}
            ]
        },
    { :id => 6, :event_type => "A", :created_at => "2013-09-25 14:44:24 UTC"}
]

I need the overall object to keep the nested objects sorted by date, but if there are 2 or more events clustered together, I want them turned into an array, as seen by the sample output. It seems like a simple problem, but I can't seem to figure it out.

To keep order use chunk method of Enumerable :

@events = [ 
 { :id => 1, :event_type => "B", :created_at => "2013-09-30 14:44:24 UTC"},
 { :id => 1, :event_type => "A", :created_at => "2013-09-29 14:44:24 UTC"},
 { :id => 1, :event_type => "A", :created_at => "2013-09-28 14:44:24 UTC"},
 { :id => 1, :event_type => "C", :created_at => "2013-09-27 14:44:24 UTC"},
 { :id => 1, :event_type => "C", :created_at => "2013-09-26 14:44:24 UTC"},
 { :id => 1, :event_type => "A", :created_at => "2013-09-25 14:44:24 UTC"}
]

p @events.chunk{|el| el[:event_type]}.map{|gr| gr[1]}

Output:

#=> [[{:id=>1, :event_type=>"B", :created_at=>"2013-09-30 14:44:24 UTC"}], 
#=>  [{:id=>1, :event_type=>"A", :created_at=>"2013-09-29 14:44:24 UTC"}, 
#=>   {:id=>1, :event_type=>"A", :created_at=>"2013-09-28 14:44:24 UTC"}], 
#=>  [{:id=>1, :event_type=>"C", :created_at=>"2013-09-27 14:44:24 UTC"}, 
#=>   {:id=>1, :event_type=>"C", :created_at=>"2013-09-26 14:44:24 UTC"}], 
#=>  [{:id=>1, :event_type=>"A", :created_at=>"2013-09-25 14:44:24 UTC"}]]

Or, if you like to have arrays only when there 2 or more elements try this:

p @events
  .chunk{|el| el[:event_type]}
  .flat_map{|gr| gr[1].length == 1 ? gr[1] : [gr[1]]}

Output will be

#=> [
#=>    {:id=>1, :event_type=>"B", :created_at=>"2013-09-30 14:44:24 UTC"}, 
#=>   [{:id=>1, :event_type=>"A", :created_at=>"2013-09-29 14:44:24 UTC"}, 
#=>    {:id=>1, :event_type=>"A", :created_at=>"2013-09-28 14:44:24 UTC"}], 
#=>   [{:id=>1, :event_type=>"C", :created_at=>"2013-09-27 14:44:24 UTC"}, 
#=>    {:id=>1, :event_type=>"C", :created_at=>"2013-09-26 14:44:24 UTC"}], 
#=>    {:id=>1, :event_type=>"A", :created_at=>"2013-09-25 14:44:24 UTC"}
#=> ]

Update

You change output format on the fly :)

For your last variant try this:

p @events
  .chunk{|el| el[:event_type]}
  .flat_map{|gr| 
    gr[1].length == 1 ? gr[1] : {:grouped_events => gr[1]}
  }

What about

p @events.group_by{|item| item[:event_type]}.values #=>

# [[{:id=>1, :event_type=>"B", :created_at=>"2013-09-30 14:44:24 UTC"}],
# [{:id=>1, :event_type=>"A", :created_at=>"2013-09-29 14:44:24 UTC"},
#  {:id=>1, :event_type=>"A", :created_at=>"2013-09-28 14:44:24 UTC"},
#  {:id=>1, :event_type=>"A", :created_at=>"2013-09-25 14:44:24 UTC"}],
# [{:id=>1, :event_type=>"C", :created_at=>"2013-09-27 14:44:24 UTC"},
#  {:id=>1, :event_type=>"C", :created_at=>"2013-09-26 14:44:24 UTC"}]]

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