0

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 :)

4

3 Answers 3

4

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.

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

3 Comments

Thanks for the answer, yes basically the data type has to be odd because the formatted_cars array is used to populate a a select field in react form. :)
@John If he has answered your question, please consider accepting his answer, there is tic in the left top, you need to click, after clicking that button, it will become green colour.
@John I would still suggest using some well-structured model like the above, and generating the necessary "React JSON" format as a separate method. If this is a rails application, you could do this via a standard serializer; or if we're doing this in plain ruby objects then how about: class CarMake; def to_json; {name: name, value: models.map(&:name)}; end; end ... Although my point still stands that you've got a rather muddled up naming convention between model (should be "make"!), type (should be "model") and value (should be "makes").
0
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"]}

Comments

0

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

2 Comments

Please make this a full answer, not just a code answer with no explanation.
Thanks for the attempt but the answer is not returned in the correct format, it is supposed to be nested in an array. [{name: 'nissan', value: ['wingroad', 'sunny', 'terrano']}, {name: 'toyota', value: ['hilux']}]

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.