3

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.

3
  • Please show your query. Commented Nov 30, 2015 at 4:00
  • I have updated my question and include the query. Commented Nov 30, 2015 at 4:19
  • I'm assuming that Jordan's answer would work, but for those of us who are not so able to see the answers in our head, can you post a sample of the actually output of your query? Commented Nov 30, 2015 at 4:21

3 Answers 3

3

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, e.g.:

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
Sign up to request clarification or add additional context in comments.

6 Comments

Using shift with group_by is a slick way of doing that I've not seen before. Will remember this one for sure.
Excellent. Thank you so much! No need for me to loop in the result.
Well, in any case you're iterating over the result. You're just doing it in a smarter way (with group_by, and in the first case map).
Is it possible to result with array of hash? final_array = { "USER1" => [{"c_idx" => 3, "legend" => 4}, {"c_idx" => 9, "legend" => 2},{"c_idx" => 3, "legend" => 1},], "USER2" => [{"c_idx" => 0, "legend" => 4}, {"c_idx" => 9, "legend" => 2},{"c_idx" => 9, "legend" => 1},], }
You can convert the output you originally wanted to the hash you mention in your comment (which you may wish to call final_hash :-)) with the following (among many ways of doing it), where final_array is your original result returned: final_array.merge(final_array) {|*,a| a.map {|h| h.values_at("c_idx", "legend")}}. This uses the form of Hash#merge that uses a block to determine the values of keys that are present in both hashes being merged, which here is all keys.
|
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] }

2 Comments

Thanks for the quick response.
Did this work for you? If so please accept the answer
1

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]]}

1 Comment

Thanks for the quick response.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.