Ruby删除哈希数组中的重复条目,但基于多个值

时间:2013-12-13 12:01:18

标签: ruby-on-rails ruby activerecord ruby-2.0

我见过很多关于此的问题,但只有一个键,从不用于多个键。

我有以下哈希数组:

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在此不起作用,因为持续时间不同或甚至不存在。我在数据库中设置了一个唯一的密钥,不允许使用相同名称,艺术家和作曲家的重复条目,所以当人们有这三个密钥的重复条目时,我有时会遇到错误。

有没有办法运行会检查这3个密钥的uniq我试过这样一个块:

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

但忽略了密钥不存在的任何内容(例如,没有作曲家的任何条目都不符合上述条件。)

我总是可以只使用:name键,但这意味着我要删除具有相同标题但不同艺术家或作曲家的编辑中的潜在有效曲目。

这是Ruby 2.0。

3 个答案:

答案 0 :(得分:16)

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)

答案 1 :(得分:4)

如果我理解你的问题,只需在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] || '')因为我们无法将nil连接到String,所以|| ''会返回一个空字符串。

答案 2 :(得分:0)

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

a.uniq {|hash| hash.values_at(:name, :composer, :artist)}