用于将唯一项插入数组的Ruby条件

时间:2015-07-23 21:25:50

标签: ruby-on-rails ruby

我知道如果你有一个array并将其引用为array.uniq,它将返回没有任何重复项。

然而在这种情况下,它是一个对象数组(正确的红宝石说话吗?)。我希望每次调用都进入@calls数组,除非call.from与数组中已存在的call_formatted对象相同。

如果数组中没有其他对象具有相同的call.from值,如何有条件地将这些对象放在数组中?

calls_raw.each do |call|       
        call_formatted = {
              :date => date,
              :time => time,
              :from => call.from,
              :duration => call.duration,
              :recording => recording,
        }
        @calls << call_formatted
end

3 个答案:

答案 0 :(得分:5)

array.uniq { |item| item[:from] }

答案 1 :(得分:1)

使用#map为您构建阵列并在其上调用#uniq ...

calls_raw.map do |call|       
        {
              :date => date,
              :time => time,
              :from => call.from,
              :duration => call.duration,
              :recording => recording,
        }
end.uniq{|call| call[:from]}

上述方法将首先构建一个大于最终可能需要的调用数组,最后调用#uniq将使该列表唯一。

或者,为了避免在数组中添加所有重复项,您可以使用Hash构建它:

calls_raw.each_with_object do |call, h|       
        h[call.from] ||= {
              :date => date,
              :time => time,
              :from => call.from,
              :duration => call.duration,
              :recording => recording,
        }
end.values

Hash方法将使用第一次出现的call.from,因为它是使用||=设置的。要使用最后一次出现的call.from,请使用=的简单分配。

还建议您使用Set代替Array

要采用这种方法,您必须在我们用该字符集填充的类上实施#eql?#hash

class CallRaw
  attr_accessor :from

  def initialize(from)
    self.from = from
  end

  def eql?(o)
    # Base equality on 'from'
    o.from == self.from
  end

  def hash 
    # Use the hash of 'from' for our hash
    self.from.hash
  end
end

require 'set'
s = Set.new
 => <Set: {}>

s << CallRaw.new("Chewbaca")
 => <Set: {<CallRaw:0x00000002211888 @from="Chewbaca">}> 

# We expect now, that adding another will not grow our set any larger
s << CallRaw.new("Chewbaca")
 => <Set: {<CallRaw:0x00000002211888 @from="Chewbaca">}> 

# Great, it's not getting any bigger
s << CallRaw.new("Chewbaca")
s << CallRaw.new("Chewbaca")
 => <Set: {#<CallRaw:0x00000002211888 @from="Chewbaca">}> 

太棒了 - 套装有效!!!

现在,有趣的是,在实施#eql?#hash之后,我们现在可以使用Array#uniq而无需传入数据块。

a = Array.new

a << CallRaw.new("Chewbaca")
 => [<CallRaw:0x000000021e2128 @from="Chewbaca">] 

a << CallRaw.new("Chewbaca")
 => [<CallRaw:0x000000021e2128 @from="Chewbaca">, <CallRaw:0x000000021c2bc0 @from="Chewbaca">]

a.uniq
 => [<CallRaw:0x000000021e2128 @from="Chewbaca">]

现在,我想知道在开始回答问题之前,StackOverflow是否会因为 太多咖啡 而获奖?

答案 2 :(得分:0)

除非出于某种原因必须是数组,否则我会将数据存储在Hash中,并以from值为键。

然后通过from值查找条目变得简单快捷。您可以选择仅在没有相同键的值时插入新值,或者插入新值并让它用该键替换旧条目。

示例:

calls = Hash.new

def add(call)
  if not calls[call.from]
    calls[call.from] = call
  end
end