2

I have an array

animals = [
  [{"name" => "Alex", "spices" => "dog", "vname" => "colour", "value" => "black"}, 
   {"name" => "Alf", "spices" => "dog", "vname" => "colour", "value" => "white"}, 
   {"name" => "Sonia", "spices" => "dog", "vname" => "colour", "value" => "white"}],

  [{"name" => "Alex", "spices" => "dog", "vname" => "health", "value" => "80"}, 
   {"name" => "Alf", "spices" => "dog", "vname" => "health", "value" => "98"}],

  [{"name" => "Alex", "spices" => "dog", "vname" => "age", "value" => "12"}]
]

Every animal (Alex, Alf and Sonia) is described by colour, health and age (vname values), but Alf does not have his hash with age in third subarray and Sonia does not have her hashes with health (second subarray) and age (third subarray). I want to check if any subarray does not have "vname" pair for some dog and if doesn't to add hash like this

{"name" => "Alf", "spices" => "dog", "vname" => "age", "value" => "unknown"}

to get an array

animals = [
 [{"name" => "Alex", "spices" => "dog", "vname" => "colour", "value" => "black"},
  {"name" => "Alf", "spices" => "dog", "vname" => "colour", "value" => "white"}, 
  {"name" => "Sonia", "spices" => "dog", "vname" => "colour", "value" => "white"}],

 [{"name" => "Alex", "spices" => "dog", "vname" => "health", "value" => "80"}, 
  {"name" => "Alf", "spices" => "dog", "vname" => "health", "value" => "98"}, 
  {"name" => "Sonia", "spices" => "dog", "vname" => "health", "value" => "unknown"}],

 [{"name" => "Alex", "spices" => "dog", "vname" => "age", "value" => "12"}, 
  {"name" => "Alf", "spices" => "dog", "vname" => "age", "value" => "unknown"}, 
  {"name" => "Sonia", "spices" => "dog", "vname" => "age", "value" => "unknown"}]
]

Can You help me with this?

aBadAssCowboy
  • 2,440
  • 2
  • 23
  • 38
ag.lis
  • 31
  • 1
  • This isn't at all clear. Can you at least take a stab at making some code? Using symbol keys (`name: "Alex"`) would also trim a lot of the verbosity here. – tadman Jul 06 '17 at 06:39
  • 1
    Also do you mean "species" instead of "spices"? – tadman Jul 06 '17 at 06:40
  • 1
    I think the data structure can be optimized to solve this problem. Where is this data coming from? Is it an option to use another data structure or translating into value object an option? – spickermann Jul 06 '17 at 06:46

2 Answers2

2

Umm. Set the names in an Array. Loop through your animals array and for each of the array inside, check if has hash for all animal names. Add the default hash for those that are missing.

    animal_names = ["Alex", "Alf", "Sonia"]

    animals.each do |animals_by_vname|
      vname         = animals_by_vname.first["vname"]
      names_present = animals_by_vname.map {|i| i["name"]}
      names_missing = animal_names - names_present

      names_missing.each do |name|
        animals_by_vname << {
          "name"   => name,
          "spices" => "dog",
          "vname"  => vname,
          "value"  => "unknown"
        }
      end
    end

    puts animals # should be you wanted it.

That solves your problem.

However, I honestly believe, the data structure needs to be optimised. Maybe have a hash of arrays with animal names as keys. Like:

    {
      "Alex": { health: "good" }
      "Alf": { age: 10, health: "good" }
    }
aBadAssCowboy
  • 2,440
  • 2
  • 23
  • 38
1

Your data format is very hard to work with.

You could define an Animal class, keep an Animal.all hash and write your data in this structure:

data = [
  [{ 'name' => 'Alex', 'spices' => 'dog', 'vname' => 'colour', 'value' => 'black' },
   { 'name' => 'Alf', 'spices' => 'dog', 'vname' => 'colour', 'value' => 'white' },
   { 'name' => 'Sonia', 'spices' => 'dog', 'vname' => 'colour', 'value' => 'white' }],

  [{ 'name' => 'Alex', 'spices' => 'dog', 'vname' => 'health', 'value' => '80' },
   { 'name' => 'Alf', 'spices' => 'dog', 'vname' => 'health', 'value' => '98' }],

  [{ 'name' => 'Alex', 'spices' => 'dog', 'vname' => 'age', 'value' => '12' }]
]

class Animal
  attr_accessor :name, :species, :health, :age, :colour
  @all = {}

  class << self
    attr_reader :all

    def find_or_create_by_name(params)
      all[params['name']] ||= Animal.new(params)
    end
  end

  def initialize(params)
    @name = params['name']
    @species = params['spices']
  end

  def to_h
    {
      name: name,
      species: species,
      age: age || 'unknown',
      colour: colour || 'unknown',
      health: health || 'unknown'
    }
  end

  def to_s
    to_h.to_s
  end

  alias inspect to_s
end

data.each do |info|
  info.each do |params|
    animal = Animal.find_or_create_by_name(params)
    animal.instance_variable_set("@#{params['vname']}", params['value'])
  end
end

require 'pp'
pp Animal.all
# {"Alex"=>
#   {:name=>"Alex", :species=>"dog", :age=>"12", :colour=>"black", :health=>"80"},
#  "Alf"=>
#   {:name=>"Alf", :species=>"dog", :age=>"unknown", :colour=>"white", :health=>"98"},
#  "Sonia"=>
#   {:name=>"Sonia", :species=>"dog", :age=>"unknown", :colour=>"white", :health=>"unknown"}}
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124