6

当我调用first_array | second_array两个包含自定义对象的数组时:

first_array = [co1, co2, co3]
second_array =[co2, co3, co4]

它返回[co1, co2, co3, co2, co3, co4]。它不会删除重复项。我试图调用uniq结果,但它也不起作用。我应该怎么办?

更新:

这是自定义对象:

class Task
    attr_accessor :status, :description, :priority, :tags
    def initiate_task task_line
        @status = task_line.split("|")[0]
        @description = task_line.split("|")[1]
        @priority = task_line.split("|")[2]
        @tags = task_line.split("|")[3].split(",")
        return self
    end

    def <=>(another_task)
        stat_comp = (@status == another_task.status)
        desc_comp = (@description == another_task.description)
        prio_comp = (@priority == another_task.priority)
        tags_comp = (@tags == another_task.tags)
        if(stat_comp&desc_comp&prio_comp&tags_comp) then return 0 end
    end
end

当我创建几个 Task 类型的实例并将它们放入两个不同的数组时,当我尝试调用“|”时 在他们身上没有任何反应,它只返回包含第一个和第二个数组元素的数组,而没有删除重复项。

4

6 回答 6

4

uniq方法可以采用一个块来定义要比较对象的对象。例如:

class Task
  attr_accessor :n
  def initialize(n)
    @n = n
  end
end

t1 = Task.new(1)
t2 = Task.new(2)
t3 = Task.new(2)

a = [t1, t2, t3]

a.uniq
#=> [t1, t2, t3] # because all 3 objects are unique

a.uniq { |t| t.n }
#=> [t1, t2]     # as it's comparing on the value of n in the object
于 2014-07-15T13:57:48.923 回答
4

如果您没有实现正确的相等方法,则任何编程语言都无法知道两个对象是否不同。在 ruby​​ 的情况下,您需要实现 eql?和散列在您的类定义中,因为这些是 Array 类用于检查相等性的方法,如Ruby 的 Array docs所述:

def eql?(other_obj)
  # Your comparing code goes here
end

def hash
  #Generates an unique integer based on instance variables
end

例如:

class A

  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def eql?(other)
    @name.eql?(other.name)
  end

  def hash
    @name.hash
  end
end

a = A.new('Peter')
b = A.new('Peter')

arr = [a,b]
puts arr.uniq

从 Array 中移除 b 只留下一个对象

希望这可以帮助!

于 2013-11-04T19:56:02.267 回答
1

我从上面尝试了解决方案fsaravia,但对我没有用。我在 Ruby 2.3.1 和 Ruby 2.4.0 中都试过了。

我发现的解决方案与fsaravia发布的解决方案非常相似,只是稍作调整。所以这里是:

class A
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def eql?(other)
    hash.eql?(other.hash)
  end

  def hash
    name.hash
  end
end

a = A.new('Peter')
b = A.new('Peter')

arr = [a,b]
puts arr.uniq

请不要介意我@在我的例子中删除了。它不会影响解决方案本身。只是,IMO,没有任何理由直接访问实例变量,因为为此设置了读取器方法。

所以......我真正改变的是在我使用的方法eql?中找到的。而已!hashname

于 2017-01-20T03:56:59.253 回答
0

关于你的“更新”,这是你在做什么:

a = Task.new # => #<Task:0x007f8d988f1b78> 
b = Task.new # => #<Task:0x007f8d992ea300> 
c = [a,b]    # => [#<Task:0x007f8d988f1b78>, #<Task:0x007f8d992ea300>] 
a = Task.new # => #<Task:0x007f8d992d3e48> 
d = [a]      # => [#<Task:0x007f8d992d3e48>]  
e = c|d      # => [#<Task:0x007f8d988f1b78>, #<Task:0x007f8d992ea300>, \
                   #<Task:0x007f8d992d3e48>] 

然后建议e = [a, b, a]?如果是这样,那就是问题所在,因为a不再指向#<Task:0x007f8d988f1b78>. 你只能说e => [#<Task:0x007f8d988f1b78>, b, a]

于 2013-11-04T21:13:56.007 回答
0

如果你看一下数组#| 运算符表示它使用eql?- 方法,在Object上与方法相同==你可以在Comparable -module中通过 mixin 来定义,然后实现<=>-method,那么你将免费获得很多比较方法。

<=>操作符很容易实现:

def <=>(obj)
    return -1 if this < obj
    return 0 if this == obj
    return 1 if this > obj
end
于 2013-11-04T20:00:32.180 回答
0

我冒昧地重写了您的类并添加了需要被覆盖的方法才能使用 uniq(哈希和 eql?)。

class Task

    METHODS = [:status, :description, :priority, :tags]
    attr_accessor *METHODS

    def initialize task_line
        @status, @description, @priority, @tags = *task_line.split("|")
        @tags = @tags.split(",")
    end

    def eql? another_task
       METHODS.all?{|m| self.send(m)==another_task.send(m)}
    end

    alias_method :==, :eql? #Strictly not needed for array.uniq

    def hash
      [@status, @description, @priority, @tags].hash
    end

end


x = [Task.new('1|2|3|4'), Task.new('1|2|3|4')]
p x.size #=> 2
p x.uniq.size #=> 1
于 2013-11-04T21:17:57.503 回答