简体   繁体   中英

Rails array group_by

I have a dynamic array of object result from a database query.

USERNAME   choice_indx   legend
USER1        3             4
USER2        0             4
USER3        0             4
USER1        9             2
USER2        9             2
USER3        8             2
USER1        3             1
USER2        9             1
USER3        8             1

Query:

SELECT survey_answers.anonymous_user_id, survey_answers.choice_index, survey_questions.legend 
  FROM `survey_answers` 
  LEFT JOIN surveys ON surveys.id = survey_answers.survey_id 
  LEFT JOIN survey_questions ON survey_questions.id = survey_answers.survey_question_id 
  WHERE (survey_questions.legend IN (1,2,4)) AND (survey_answers.track_id =2) AND (survey_answers.survey_id =2) AND (surveys.survey_type =2)

How can I group this by user and result it like this:

final_array = {
  "USER1" => [[3,4],[9,2],[3,1]], 
  "USER2" => [[0,4],[9,2],[9,1]],
  "USER3" => [[0,4],[8,2],[8,1]]
}

I've tried using group_by in rails, but the result was not the same from what I want. Can anyone lend me a hand? Thanks.

Assuming objects is an Enumerable of ActiveModel objects with anonymous_user_id , choice_index , and legend attributes, this will do what you want:

objects.map {|obj| obj.values_at(:anonymous_user_id, :choice_index, :legend) }
  .group_by(&:shift)

You can skip the map if instead you use pluck in your ActiveRecord query, eg:

MyModel.where(...)
  .pluck(:anonymous_user_id, :choice_index, :legend)
  .group_by(&:shift)

Edit

In reply to your comment, yes it is, although it's not quite as clean:

MyModel.where(...)
  .pluck(:anonymous_user_id, :choice_index, :legend)
  .map {|vals| Hash[ %w[ __key__ c_idx legend ].zip(vals) ] }
  .group_by {|hsh| hsh.delete("__key__") }

Or:

MyModel.where(...)
  .pluck(:anonymous_user_id, :choice_index, :legend)
  .each_with_object(Hash.new {|h,k| h[k] = [] }) do |(key, c_idx, legend), hsh| 
    hsh[key] << { "c_idx" => c_idx, "legend" => legend }
  end

Assuming the result from the query is data_hash (array of hash), then the following will give you the desired result:

data_hash = [{ 'USERNAME' => 'USER1', 'choice_indx' => 3, 'legend' => 4 },
             { 'USERNAME' => 'USER2', 'choice_indx' => 0, 'legend' => 4 },
             { 'USERNAME' => 'USER3', 'choice_indx' => 0, 'legend' => 4 },
             { 'USERNAME' => 'USER1', 'choice_indx' => 9, 'legend' => 2 },
             { 'USERNAME' => 'USER2', 'choice_indx' => 9, 'legend' => 2 },
             { 'USERNAME' => 'USER3', 'choice_indx' => 8, 'legend' => 2 },
             { 'USERNAME' => 'USER1', 'choice_indx' => 3, 'legend' => 1 },
             { 'USERNAME' => 'USER2', 'choice_indx' => 9, 'legend' => 1 },
             { 'USERNAME' => 'USER3', 'choice_indx' => 8, 'legend' => 1 }]

final_array = Hash.new{|h,k|h[k]=[]}
data_hash.each do |data|
  final_array[data['USERNAME']] << [data['choice_indx'], data['legend']]
end

p final_array
# => {"USER1"=>[[3, 4], [9, 2], [3, 1]], "USER2"=>[[0, 4], [9, 2], [9, 1]], "USER3"=>[[0, 4], [8, 2], [8, 1]]}

I don't believe there is a rails way to do this, but with ruby you could do

# create hash with a empty array for each username 
results = Hash[@query.pluck(:username).map { |username| [username, []] }]
    @query.each { |data| results[data.username] << [data.choice_idx,
    data.legend] }

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