2013-12-13 35 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)} 
相关问题