Объединить Ruby массивы - PullRequest
1 голос
/ 08 ноября 2010

У меня есть несколько массивов объектов Ruby класса UserInfo:

class UserInfo  
    attr_accessor :name, :title, :age
end

Как я могу объединить эти массивы в один массив? Пользователь идентифицируется по его имени, поэтому я не хочу повторяющихся имен. Если имя, заголовок, возраст и т. Д. Равны, я хотел бы иметь 1 запись в новом массиве. Если имена совпадают, но любые другие детали отличаются, я, вероятно, хочу, чтобы эти 2 пользователя в другом массиве вручную исправляли ошибки.

Заранее спасибо

Ответы [ 3 ]

1 голос
/ 10 ноября 2010

Переопределите сравнение равенства на вашем объекте, и вы можете быстро избавиться от реальных дубликатов с помощью Array#uniq

class UserInfo
  attr_accessor :name, :title, :age

  def == other
    name==other.name and title==other.title and age==other.age
  end
end

# assuming a and b are arrays of UserInfo objects
c = a | b
# c will only contain one of each UserInfo

Затем вы можете сортировать по имени и искать дубликаты только по имени

d = c.sort{ |p,q| p.name <=> q.name } #sort by name
name = ""
e = []
d.each do |item|
  if item.name == name
    e[-1] = [e[-1],item].flatten 
  else
    e << item
  end
end
0 голосов
/ 10 ноября 2010

Вот еще один потенциальный способ.Если у вас есть способ идентификации каждого UserInfo, скажем метод to_str, который печатает значения:

  def to_str()
    return "#{@name}:#{@title}:#{@age}"
  end

Вы можете использовать inject и хеш

all_users = a + b # collection of users to "merge"    
res = all_users.inject({})do |h,v|
  h[v.to_str] = v  #save the value indexed on the string output
  h # return h for the next iteration
end

merged = res.values #the unique users
0 голосов
/ 08 ноября 2010

Год назад Я залатал обезьяну своего рода загадочный instance_variables_compare на Object Я думаю, вы могли бы использовать это.

class Object
  def instance_variables_compare(o)
    Hash[*self.instance_variables.map {|v|
      self.instance_variable_get(v)!=o.instance_variable_get(v) ? 
      [v,o.instance_variable_get(v)] : []}.flatten]
  end
end

Дрянной пример

require 'Date'

class Cheese
  attr_accessor :name, :weight, :expire_date
  def initialize(name, weight, expire_date)
    @name, @weight, @expire_date = name, weight, expire_date
  end
end

stilton=Cheese.new('Stilton', 250, Date.parse("2010-12-02"))
gorgonzola=Cheese.new('Gorgonzola', 250, Date.parse("2010-12-17"))

irb - мое оружие выбора

>> stilton.instance_variables_compare(gorgonzola)
=> {"@name"=>"Gorgonzola", "@expire_date"=>#<Date: 4910305/2,0,2299161>}
>> gorgonzola.instance_variables_compare(stilton)
=> {"@name"=>"Stilton", "@expire_date"=>#<Date: 4910275/2,0,2299161>}
>> stilton.expire_date=gorgonzola.expire_date
=> #<Date: 4910305/2,0,2299161>
>> stilton.instance_variables_compare(gorgonzola)
=> {"@name"=>"Gorgonzola"}
>> stilton.instance_variables_compare(stilton)
=> {}

Как видите, instance_variables_compare возвращает пустой хэш, если два объекта имеют одинаковое содержимое.

сырный массив

stilton2=Cheese.new('Stilton', 210, Date.parse("2010-12-02"))
gorgonzola2=Cheese.new('Gorgonzola', 250, Date.parse("2010-12-17"))

arr=[]<<stilton<<stilton2<<gorgonzola<<gorgonzola2

Один хеш без проблем и один с

h={}
problems=Hash.new([])

arr.each {|c| 
  if h.has_key?(c.name)
    if problems.has_key?(c.name)
      problems[c.name]=problems[c.name]<<c
    elsif h[c.name].instance_variables_compare(c) != {}
      problems[c.name]=problems[c.name]<<c<<h[c.name]
      h.delete(c.name)
    end
  else 
    h[c.name]=c
  end
}

Теперь Hash h содержит объекты без проблем слияния, а хэш problems содержит объекты с переменными экземпляра, которые отличаются.

>> h
=> {"Gorgonzola"=>#<Cheese:0xb375e8 @name="Gorgonzola", @weight=250, @expire_date=#<Date: 2010-12-17 (4911095/2,0,2299161)>>}

>> problems
=> {"Stilton"=>[#<Cheese:0xf54c30 @name="Stilton", @weight=210, @expire_date=#<Date: 2010-12-02 (4911065/2,0,2299161)>>, #<Cheese:0xfdeca8 @name="Stilton", @weight=250,@expire_date=#<Date: 2010-12-02 (4911065/2,0,2299161)>>]}    

Насколько я понимаю, вам вообще не придется изменять этот код для поддержки массива UserInfo объектов.

Скорее всего, было бы намного быстрее сравнивать свойства напрямую или с переопределением ==. Вот как вы переопределяете ==

def ==(other)
  return self.weight == other.weight && self.expire_date == other.expire_date
end

и цикл превращается в

arr.each {|c| 
  if h.has_key?(c.name)
    if problems.has_key?(c.name)
      problems[c.name]=problems[c.name]<<c
    elsif h[c.name] != c
      problems[c.name]=problems[c.name]<<c<<h[c.name]
      h.delete(c.name)
    end
  else 
    h[c.name]=c
  end
}

Наконец, вы можете преобразовать Hash обратно в Array

result = h.values
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...