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

kak*_*bei 6 ruby activerecord ruby-on-rails 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"}]
Run Code Online (Sandbox Code Playgroud)

a.uniq在这里不起作用,因为持续时间不同或甚至不存在.我在数据库中设置了一个唯一的密钥,不允许使用相同名称,艺术家和作曲家的重复条目,因此当人们对这3个密钥有重复的条目时,我有时会遇到错误.

有没有办法运行uniq,检查这3个键?我试过像这样的块:

 new_tracks.uniq do |a_track|
   a_track[:name]
   a_track[:artist]
   a_track[:composer]
 end
Run Code Online (Sandbox Code Playgroud)

但是忽略了密钥不存在的任何东西(没有作曲家的任何条目都不符合上述标准).

我总是可以只使用:name密钥,但这意味着我将摆脱具有相同标题但不同的艺术家或作曲家的编辑中的潜在有效曲目.

这是Ruby 2.0.

Sim*_*tti 16

uniq接受一个块.如果给出了一个块,它将使用块的返回值进行比较.

您的代码接近解决方案,但在您的代码中,返回值仅a_track[:composer]是最后一次计算的语句.

您可以将所需的属性加入到字符串中并返回该字符串.

new_tracks.uniq { |track| [track[:name], track[:artist], track[:composer]].join(":") }
Run Code Online (Sandbox Code Playgroud)

可能的重构是

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

或者在模型中添加执行连接的自定义方法,然后调用它

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

new_tracks.uniq(&:digest)
Run Code Online (Sandbox Code Playgroud)


suj*_*jay 6

另一种方法是使用values_at。如果您不想使用切片和连接

a.uniq {|hash| hash.values_at(:name, :composer, :artist)}
Run Code Online (Sandbox Code Playgroud)


the*_*Man 5

如果我理解您的问题,那只是在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],
  ]
} 
Run Code Online (Sandbox Code Playgroud)

哪个会返回:

[
  {: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"}
]
Run Code Online (Sandbox Code Playgroud)

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"}]
Run Code Online (Sandbox Code Playgroud)

我必须使用,(a_track[:artist] || '')因为我们无法将a连接nil到String,所以|| ''返回一个空字符串。