peter
peter

Reputation: 42207

use of index with a hash instead of an array

I use in a genealogy program following method to add a mariage/spouse to a Person. @mariages is an array of arrays.

def add_spouse(spouse, mariage_date = nil, divorce_date = nil)
  @mariages.push([spouse, mariage_date, divorce_date]) unless @mariages.index{|(a, b, c)| a == spouse && b == mariage_date} 
  spouse.mariages.push(self) unless spouse.mariages.index{|(a, b, c)| a == self && b == mariage_date} 
end

With the unless @mariages.index{|(a, b, c)| a == spouse && b == mariage_date} i check if the mariage is not allready in the array.

Now i want to keep my mariages in an array of hashes like this

def add_spouse(spouse, mariage_date = nil, divorce_date = nil)
    @mariages.push({:spouse => spouse, :mariage_date => mariage_date, :divorce_date => divorce_date}) unless ...
    spouse.mariages.push({:spouse => self, :mariage_date => mariage_date, :divorce_date => divorce_date}) unless ... 
end

Can someone help me adapt the unless part to do the check if the hash is not allready presant in the array ?

Upvotes: 0

Views: 78

Answers (3)

Paul Rubel
Paul Rubel

Reputation: 27232

As per your question in the commend I'd do something like the following. Note that Dates should use the date class and the partners in the marriage should be classes too, with births and deaths and whatnot. But hopefully you can see how moving the data into objects that know what to do with it, can simplify the design as things get larger. (I also went with bride and groom for simplicity, feel free to change that in a genealogically approved manner).

Each person would have a Marriages associated with them and if you had a bride and a groom they could share a single Marriage, but have a different list of Marriages.

class Marriage
  attr_accessor :marriage_date, :divorce_date, :bride, :groom

  def initialize(date, bride, groom)
    @marriage_date = date
    @bride = bride
    @groom = groom
  end

  def marriage_equals(m)
    return (@marriage_date == m.marriage_date) && 
           (@bride == m.bride) &&
           (@groom == m.groom)
  end    
end

class Marriages
  def initialize
    @marriages = []
  end

  def add_marriage(marriage)
    if (@marriages.any? { |m| m.marriage_equals(marriage) })
      puts "Marriage of #{marriage.groom} already listed"
      return false
    else
      puts "Added new marriage"
      @marriages.push(marriage)
      return true
    end
  end

end


m1 = Marriage.new("1-1-0002", "Wilma", "Fred")
m2 = Marriage.new("6-8-0003", "Betty", "Barney")
m3 = Marriage.new("2-8-8003", "Jane", "George")


marriages = [m1,m2]
p marriages.any? { |m| m.marriage_equals(m1) } # true
p marriages.any? { |m| m.marriage_equals(m3) } # false

m_list = Marriages.new
m_list.add_marriage(m1) # Added new marriage
m_list.add_marriage(m2) # Added new marriage
m_list.add_marriage(m2) # Marriage of Barney already listed

Upvotes: 1

Roland Mai
Roland Mai

Reputation: 31087

In the block that goes into index the element getting iterated over is a hash so you should use

.. unless @mariages.index{|h| h[:spouse] == spouse && h[:mariage_date] == mariage_date}

and

.. unless spouse.mariages.index{|h| h[:spouse] == self && h[:mariage_date] == mariage_date} 

PS: mariage is misspelled. It should be marriage.

Upvotes: 1

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230411

Since your array now contains hashes instead of other arrays, you can't use "array unpacking" (not sure what's the official term for that is). You will get an instance of hash, and you can access it as you normally would.

 @mariages.push({:spouse => spouse, 
                 :mariage_date => mariage_date, 
                 :divorce_date => divorce_date}) unless @mariages.index{|h| h[:spouse] == spouse && h[:mariage_date] == mariage_date}

Upvotes: 1

Related Questions