Wat is in Rails de beste manier om een ​​hash in een genestelde hash te verdichten

Stel dat ik dit heb:

[
  { :user_id => 1, :search_id => a},
  { :user_id => 1, :search_id => b},
  { :user_id => 2, :search_id => c},
  { :user_id => 2, :search_id => d}
]

en ik wil eindigen met:

[
  { :user_id => 1, :search_id => [a,b]},
  { :user_id => 2, :search_id => [c,d]}
]

Wat is de beste manier om dat te doen?

6

5 antwoord

Zeer vreemde eis inderdaad. Hoe dan ook

[ { :user_id => 1, :search_id => "a"},
  { :user_id => 1, :search_id => "b"},
  { :user_id => 2, :search_id => "c"},
  { :user_id => 2, :search_id => "d"} ] \
    .map{ |h| h.values_at(:user_id, :search_id) } \
    .group_by(&:first) \
    .map{ |k, v| { :user_id => k, :search_id => v.map(&:last) } }
7
toegevoegd
Ooh, veel mooier! 1
toegevoegd de auteur Matchu, de bron
array.group_by{|x| x[:user_id] }.values.map do |val|
  { user_id:   val.first[:user_id], 
    search_id: val.inject([]){|me, el| me << el[:search_id]} }
end
1
toegevoegd

Allereerst denk ik dat de schonere uitvoerstructuur hier is om de gebruikers-ID's als hash-sleutels en de lijst met zoek-ID's als waarden te laten gelden:

{
  1 => [a, b],
  2 => [c, d]
}

Misschien is er een slimme manier om Rails-helpers te gebruiken om deze structuur te krijgen, maar het is niet ook erg om het handmatig te doen, hetzij:

output = {}
input.each do |row|
  key = row[:user_id]
  value = row[:search_id]

  output[key] ||= []
  output[key] << value
end
0
toegevoegd
input.inject({}) do
  |m, h| (m[h[:user_id]] ||= []) << h[:search_id]; m
end.inject([]) { |m, (k, v)| m << { :user_id => k, :search_id => v }; m }
0
toegevoegd

Ik ben het eens met Matchus-representatie van de gegevens en wil alleen een kortere versie voorstellen (waarbij invoer de eerste array is).

input.each.with_object(Hash.new {|h,k| h[k] = []}) do |k,o|
  o[k[:user_id]] << k[:search_id]
end

EDIT: this is ruby > 1.9

0
toegevoegd