2010-11-08 142 views
1

UserInfo類的Ruby對象的幾個數組:合併紅寶石陣列

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

我怎麼能這些數組合併成一個數組?用戶名是由其名稱標識的,所以我不想重複名稱。如果名稱,標題,年齡等相等,我希望在新陣列中有1個條目。如果名稱相同,但任何其他細節不同,我可能希望在不同陣列中的這2個用戶手動修復錯誤。

在此先感謝

+0

當您確實需要刪除重複項時,您可以使用動詞合併。當你詢問數組時,你也用'hash'標記了這個。你應該編輯這個問題,使其更清晰。 – Olly 2010-11-08 13:02:35

+0

@Olly:將標記從「散列」更改爲「數組」。 – 2010-11-08 13:53:58

回答

0

一年前I monkey patchedObject一種神祕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 
+0

WOW,thx Jonas;) – 2010-11-08 14:49:48

+0

它主要是從舊博客文章複製和粘貼。我也糾正了一個錯誤。 – 2010-11-08 15:13:12

+0

它解決了你的問題嗎? – 2010-11-10 13:09:35

0

這是另一個潛在方法。如果您有標識每個的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 
1

重新定義你的對象平等的比較,和你也能很快擺脫實際的複製與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