In Rails, the best way to compress a hash into a nested hash

Say I have this:

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

and I want to end up with:

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

What is the best way to do this?

+6
source share
5 answers

Very strange requirement. Anyway

 [ { :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
source
 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
source

First, I think a cleaner output structure is to allow user identifiers to be hash keys, and a list of search identifiers to be values:

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

There may be a reasonable way to use Rails helpers to get this structure, but it is not so bad to do it manually:

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

I agree with the presentation of Matchus data and just want to offer a shorter version for it (input is the original array).

 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
source
 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
source

Source: https://habr.com/ru/post/903215/


All Articles