2013-12-13 36 views
6

我已經看到了許多有關此問題的問題,但僅使用一個鍵,從不使用多個鍵。Ruby刪除哈希數組中的重複條目,但基於多個值

我有散列以下陣列:由於持續時間不同,也可能根本不存在

a = [{:name=>"Yes, Yes, Yes", :artist=>"Some Dude", :composer=> 'First Dude', :duration=>"3:21"}, 
{:name=>"Chick on the Side", :artist=>"Another Dude", :duration=>"3:20"}, 
{:name=>"Luv Is", :duration=>"3:13"}, 
{:name=>"Yes, Yes, Yes", :artist=>"Some Dude", :composer=> 'First Dude', :duration=>"2"}, 
{:name=>"Chick on the Side", :artist=>"Another Dude"}] 

a.uniq不會在這裏工作。我在數據庫中設置了一個獨一無二的密鑰,該密鑰不允許由同名,藝術家和作曲家重複錄入,所以我有時會在人們對這三個密鑰有重複條目時發生錯誤。

有沒有辦法運行uniq來檢查這3個鍵?我試圖像這樣的塊:

new_tracks.uniq do |a_track| 
    a_track[:name] 
    a_track[:artist] 
    a_track[:composer] 
end 

但是,忽略任何其中鍵是不存在(沒有作曲家不符合例如上述標準的任何條目)。

我總是可以使用:name這個鍵,但這意味着我將編輯中具有相同標題但不同藝術家或作曲家的潛在有效曲目刪除。

這是與Ruby 2.0。

回答

13

uniq接受一個塊。如果給出了一個塊,它將使用塊的返回值進行比較。

您的代碼已接近解決方案,但在您的代碼中,返回值僅爲a_track[:composer],這是最後一次評估的語句。

您可以將所需的屬性加入到字符串中並返回該字符串。

new_tracks.uniq { |track| [track[:name], track[:artist], track[:composer]].join(":") } 

一種可能的重構是

new_tracks.uniq { |track| track.attributes.slice('name', 'artist', 'composer').values.join(":") } 

或者在模型執行聯接添加自定義方法,並調用它

class Track < ActiveRecord::Base 
    def digest 
    attributes.slice('name', 'artist', 'composer').values.join(":") 
    end 
end 

new_tracks.uniq(&:digest) 
+0

太好了,**非常感謝!**第一個工作正常:'.uniq {| track | [track [:name],track [:artist],track [:composer]]。join(「:」)}'。第二個給我一個錯誤'SyntaxError:unexpected'}',期待']''。如果我解決了這個問題,那麼我會得到'未定義的方法'屬性''。但第一個人就是這樣做的。再次感謝。 – kakubei

+0

我修正了語法錯誤。 –

+0

我仍然得到'未定義的方法'屬性''該行...... – kakubei

2

如果我明白你的問題,它只是一個在uniq區塊內使用正確的數據組合的問題:

a = [ 
    {:name=>"Yes, Yes, Yes", :artist=>"Some Dude", :composer=> 'First Dude', :duration=>"3:21"}, 
    {:name=>"Yes, Yes, Yes", :artist=>"Some Dude", :composer=> 'First Dude', :duration=>"2"}, 
    {:name=>"Chick on the Side", :artist=>"Another Dude", :duration=>"3:20"}, 
    {:name=>"Chick on the Side", :artist=>"Another Dude"}, 
    {:name=>"Luv Is", :duration=>"3:13"}, 
] 

a.uniq{ |a_track| 
    [ 
    a_track[:name], 
    a_track[:artist], 
    a_track[:composer], 
    ] 
} 

這將返回:

[ 
    {:name=>"Yes, Yes, Yes", :artist=>"Some Dude", :composer=>"First Dude", :duration=>"3:21"}, 
    {:name=>"Chick on the Side", :artist=>"Another Dude", :duration=>"3:20"}, 
    {:name=>"Luv Is", :duration=>"3:13"} 
] 

uniq使我們創造了塊內任何事情,用的是它的比較。我選擇使用一個數組,因爲Ruby知道如何比較數組,但該值可能是一個MD5校驗或CRC校驗,如果這是有道理的:

a.uniq{ |a_track| 
    OpenSSL::Digest::MD5.digest(a_track[:name] + (a_track[:artist] || '') + (a_track[:composer] || '')) 
} 
# => [{:name=>"Yes, Yes, Yes", :artist=>"Some Dude", :composer=>"First Dude", :duration=>"3:21"}, {:name=>"Chick on the Side", :artist=>"Another Dude", :duration=>"3:20"}, {:name=>"Luv Is", :duration=>"3:13"}] 

我必須使用(a_track[:artist] || '')因爲我們可以」 t將nil連接到字符串,因此|| ''會返回空字符串。

+0

這很有趣,我喜歡這種方法:'a.uniq {| a_track | [a_track [:name],a_track [:artist],a_track [:composer]]}'我首先看到了Simone的回答,所以我接受了這個答案,但我更喜歡這個。非常感謝。 – kakubei

+0

您也可以'to_s'每個值,它應該將任何nils轉換爲空字符串。 –

+0

我們可以,但它隱藏了意圖。 –

0

另一種方法是使用values_at。如果你不想使用切片並加入

a.uniq {|hash| hash.values_at(:name, :composer, :artist)} 
相關問題