我UserInfo
類的Ruby對象的幾個數組:合併紅寶石陣列
class UserInfo
attr_accessor :name, :title, :age
end
我怎麼能這些數組合併成一個數組?用戶名是由其名稱標識的,所以我不想重複名稱。如果名稱,標題,年齡等相等,我希望在新陣列中有1個條目。如果名稱相同,但任何其他細節不同,我可能希望在不同陣列中的這2個用戶手動修復錯誤。
在此先感謝
我UserInfo
類的Ruby對象的幾個數組:合併紅寶石陣列
class UserInfo
attr_accessor :name, :title, :age
end
我怎麼能這些數組合併成一個數組?用戶名是由其名稱標識的,所以我不想重複名稱。如果名稱,標題,年齡等相等,我希望在新陣列中有1個條目。如果名稱相同,但任何其他細節不同,我可能希望在不同陣列中的這2個用戶手動修復錯誤。
在此先感謝
一年前I monkey patched上Object
一種神祕instance_variables_compare
。我想你可以使用它。
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
}
現在哈希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
WOW,thx Jonas;) – 2010-11-08 14:49:48
它主要是從舊博客文章複製和粘貼。我也糾正了一個錯誤。 – 2010-11-08 15:13:12
它解決了你的問題嗎? – 2010-11-10 13:09:35
這是另一個潛在方法。如果您有標識每個的UserInfo的一種方式,說打印出值的to_str方法:
def to_str()
return "#{@name}:#{@title}:#{@age}"
end
您可以使用注入和哈希
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
重新定義你的對象平等的比較,和你也能很快擺脫實際的複製與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
當您確實需要刪除重複項時,您可以使用動詞合併。當你詢問數組時,你也用'hash'標記了這個。你應該編輯這個問題,使其更清晰。 – Olly 2010-11-08 13:02:35
@Olly:將標記從「散列」更改爲「數組」。 – 2010-11-08 13:53:58