0

I simply want to group cities by their state and from there have the array of the hash key (i.e. State Name) return an array of hash data pertaining to it's cities. Right now I have something like this:

City.all.group_by { |c| c.state.name }

Which will return:

{
  "Illinois": [# < City id: 3, name: "Chicago", state_id: 3 > ],
  "Texas": [# < City id: 2, name: "Houston", state_id: 2 > ],
  "California": [# < City id: 1, name: "Los Angeles", state_id: 1 > ],
  "New York": [# < City id: 4, name: "New York City", state_id: 4 > ]
}

Notice how it returns an array of rails objects. Instead I want to return an array of hashes with certain attributes, like their id and name.

6
  • Why do you want hashes instead of model instances? Commented Dec 4, 2019 at 17:20
  • @Stefan I want to later export the data to json. Commented Dec 4, 2019 at 17:21
  • You might want to take a look at github.com/rails/jbuilder Commented Dec 4, 2019 at 17:22
  • Interesting. It'll group them the way I want too? Commented Dec 4, 2019 at 17:25
  • You still have to group the data yourself (in your controller). But you don't have to prepare a huge hash representing the entire JSON result. Instead you have a nice DSL to define your JSON (in your view). Commented Dec 4, 2019 at 17:34

3 Answers 3

2

The reason the grouped values are Rails objects (your models) is due to the fact that you also start with these objects. You can use the attributes method to retrieve the attributes of a model instance as a hash.

The following achieves the result you want:

City.all.group_by { |city| city.state.name }
  .transform_values { |cities| cities.map(&:attributes) }

If you only want specific attributes, use slice instead:

City.all.group_by { |city| city.state.name }
  .transform_values { |cities| cities.map { |city| city.slice(:id, :name) } }

Note that slice will return an ActiveSupport::HashWithIndifferentAccess instance. Which mostly can be used in the same manner as a normal hash, but returns the same value for both hash[:name] and hash['name']. If you rather use a normal hash append a to_hash call after the slice call.

Sign up to request clarification or add additional context in comments.

Comments

0

This should be enough for you

City.all.group_by { |c| c.state.name }.map {|k,v| [k, v.attributes] }.to_h

and to select only specified attributes do

v.attributes.slice(:name, :id)

1 Comment

You currently call attributes with an array as receiver, since each value is an array of cities. Resulting in a NoMethodError. Furthermore your slice(:name, :id) should be slice('name', 'id'), since attributes returns a hash where the keys are strings, not symbols.
0

One of the easiest approach is to convert it into json object

City.all.as_json.group_by { |c| c.state.name }

this will fix the issue

Comments

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.