John
John

Reputation: 43

How do I format an array of objects with ruby?

I am a bit lost on how to format this array of objects.

Initial array:

cars = [{model: 'nissan', type: 'wingroad'},{model: 'nissan', type: 'sunny'},{model: 'nissan', type: 'terrano'},{model: 'toyota', type: 'hilux'}]

Expected Output:

formatted_cars = [{name: 'nissan', value: ['wingroad', 'sunny', 'terrano']}, {name: 'toyota', value: ['hilux']

Would I map over the results and apply an inject then apply some cunning merge techniques for that formatted array. Or is there another way to go about it?

I am new to ruby and would love any help, thanks :)

Upvotes: 0

Views: 3727

Answers (3)

sawa
sawa

Reputation: 168249

Yes, there is, as follows:

cars.group_by{|h| h[:model]}.transform_values{|v| v.map{|h| h[:type]}}
#=> {"nissan"=>["wingroad", "sunny", "terrano"], "toyota"=>["hilux"]}

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110755

cars = [{model: 'nissan', type: 'wingroad'},
        {model: 'nissan', type: 'sunny'},
        {model: 'nissan', type: 'terrano'},
        {model: 'toyota', type: 'hilux'}]

cars.each_with_object({}) { |g,h| (h[g[:model]] ||= []) << g[:type] }.
     map { |k,v| { model:k, type:v } }
  #=> [{:model=>"nissan", :type=>["wingroad", "sunny", "terrano"]},
  #    {:model=>"toyota", :type=>["hilux"]}]

Note that

cars.each_with_object({}) { |g,h| (h[g[:model]] ||= []) << g[:type] }
  #=> {"nissan"=>["wingroad", "sunny", "terrano"], "toyota"=>["hilux"]}

Upvotes: 0

Tom Lord
Tom Lord

Reputation: 28305

There are many ways to do this. Here is one;

cars = [
  {model: 'nissan', type: 'wingroad'},
  {model: 'nissan', type: 'sunny'},
  {model: 'nissan', type: 'terrano'},
  {model: 'toyota', type: 'hilux'}
]

cars
  .group_by { |car| car[:model] }
  .map { |model, cars| {name: model, value: cars.map { |car| car[:type] }} }

...However, why are you starting with such an odd data format, and aiming to finish with another odd data format? (By "odd", I basically mean relying on an array of hashes of arrays to store the data.)

There may be a good reason for this (e.g. integration with a 3rd party API), but otherwise I would suggest making this somehow more object-oriented and using classes to define the make/model of the cars. For example, perhaps something like:

# e.g. Nissan
class CarMake
  attr_reader :name, :models
  def initialize(name)
    @name = name
    @models = []
  end

  def add_model(name)
    model = CarModel.new(name)
    @models << model
    model.make = self
  end
end

# e.g. WingRoad
class CarModel
  attr_reader :name
  attr_accessor :make
  def initialize(name)
    @name = name
  end
end

# Assuming we still need to start with this data structure!
cars = [
  {model: 'nissan', type: 'wingroad'},
  {model: 'nissan', type: 'sunny'},
  {model: 'nissan', type: 'terrano'},
  {model: 'toyota', type: 'hilux'}
]

car_makes = {}
cars.each do |car|
  car_makes[car[:model]] ||= CarMake.new(car[:model])
  car_makes[car[:model]].add_model(car[:type])
end

This is just one of many possible ways to organise the code, and although it may be a little more complex to understand at first, the resulting data structure is much more useful:

car_makes
=> {"nissan"=>
  #<CarMake:0x00007ff2ee44ad20
   @models=
    [#<CarModel:0x00007ff2ee44aca8 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="wingroad">,
     #<CarModel:0x00007ff2ee44ac80 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="sunny">,
     #<CarModel:0x00007ff2ee44ac58 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="terrano">],
   @name="nissan">,
 "toyota"=>#<CarMake:0x00007ff2ee44ac30 @models=[#<CarModel:0x00007ff2ee44ab68 @make=#<CarMake:0x00007ff2ee44ac30 ...>, @name="hilux">], @name="toyota">}

car_makes['nissan'].models
=> [#<CarModel:0x00007ff2ee44aca8 @make=#<CarMake:0x00007ff2ee44ad20 @models=[...], @name="nissan">, @name="wingroad">,
 #<CarModel:0x00007ff2ee44ac80 @make=#<CarMake:0x00007ff2ee44ad20 @models=[...], @name="nissan">, @name="sunny">,
 #<CarModel:0x00007ff2ee44ac58 @make=#<CarMake:0x00007ff2ee44ad20 @models=[...], @name="nissan">, @name="terrano">]

car_makes['nissan'].models.first
=> #<CarModel:0x00007ff2ee44aca8
 @make=
  #<CarMake:0x00007ff2ee44ad20
   @models=
    [#<CarModel:0x00007ff2ee44aca8 ...>,
     #<CarModel:0x00007ff2ee44ac80 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="sunny">,
     #<CarModel:0x00007ff2ee44ac58 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="terrano">],
   @name="nissan">,
 @name="wingroad">

car_makes['nissan'].models.first.make
=> #<CarMake:0x00007ff2ee44ad20
 @models=
  [#<CarModel:0x00007ff2ee44aca8 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="wingroad">,
   #<CarModel:0x00007ff2ee44ac80 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="sunny">,
   #<CarModel:0x00007ff2ee44ac58 @make=#<CarMake:0x00007ff2ee44ad20 ...>, @name="terrano">],
 @name="nissan">

...And so on. We now have a structured data, instead of just arbitrary (and mis-named!) mixes of arrays and hashes that are messy to manipulate.

Upvotes: 4

Related Questions