Ruby 如何从具有自定义对象的阵列中删除重复项

Ruby 如何从具有自定义对象的阵列中删除重复项,ruby,arrays,object,union,Ruby,Arrays,Object,Union,在包含自定义对象的两个数组上调用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, :tag

在包含自定义对象的两个数组上调用
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
类任务
属性访问器:状态,:描述,:优先级,:标记
def启动任务行
@状态=任务_行。拆分(“|”)[0]
@description=task_line.split(“|”)[1]
@优先级=任务_行。拆分(“|”)[2]
@tags=task_line.split(“|”)[3]。split(“,”)
回归自我
结束
def(另一个_任务)
stat_comp=(@status==另一个_task.status)
desc_comp=(@description==另一个任务。description)
prio_comp=(@priority==另一个任务。priority)
tags_comp=(@tags==另一个_task.tags)
如果(stat_comp&desc_comp&prio_comp&tags_comp),则返回0结束
结束
结束

当我创建几个任务类型的实例并将它们放入两个不同的数组中时,当我尝试对它们调用“|”时,什么也不会发生,它只返回数组,包括第一个和第二个数组的元素,而不删除重复项。

如果不实现,任何编程语言都无法识别两个对象是否不同正确的相等方法。 对于ruby,您需要实现eql吗?以及类定义中的哈希,因为这些是数组类用于检查相等性的方法,如上所述:

例如:

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
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
从数组中删除b,只留下一个对象

希望这有帮助

如果您查看操作符,它会说它使用了
eql?
-方法,该方法与
=
方法相同。您可以通过在-模块中混合来定义它,然后实现
-方法,然后您将免费获得许多比较方法

操作符非常容易实现:

def <=>(obj)
    return -1 if this < obj
    return 0 if this == obj
    return 1 if this > obj
end
def(obj)
如果此值小于obj,则返回-1
如果this==obj,则返回0
如果此>obj,则返回1
结束

关于您的“更新”,您是这样做的:

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>] 
a=Task.new=
b=任务。新建#=>#
c=[a,b]=>
a=任务。新建#=>#
d=[a]#=>
e=c | d#=>\
#] 

然后建议
e=[a,b,a]
?如果是这样,那就是问题所在,因为
a
不再指向
#
。您所能说的就是
e=>[#,b,a]

为了使用uniq(hash和eql?),我冒昧地重写了您的类并添加了需要重写的方法


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

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
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

我在上面的
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
请不要介意我删除了示例中的
@
。它不会影响解决方案本身。只是,在我看来,没有任何理由直接访问实例变量,因为为此设置了一个reader方法


所以…我真正改变的是在
eql?
方法中找到的,在那里我使用了
hash
而不是
name
。就这样

返回的是什么?[co1,co2,co3,co2,co3,co4]?这些对象似乎是不同的对象。@dax first_array | second_array Sergio我猜它们有不同的内存地址。这不像将简单数据作为整数进行比较。它们是什么类型的对象?你想基于什么来比较它们?你的目标是什么-比较对象或获得uniq值?这是我必须在对象上实现的运算符,以便我可以在包含此类对象的两个数组上调用union?这是使类可枚举的要求。为了评估
==
,定义
==
就足够了。实际上,sawa的评论让我意识到我的答案是错误的,数组类需要eql吗?为了识别唯一的对象,我更新了我的答案。你认为实现“==”就足够调用两个数组的联合操作了吗?这两个数组只包含该类型的自定义对象吗?当然,但它同样容易实现,而且它免费提供了所有的比较方法(包括
==
)。我只尝试了“==”,但它不起作用。当我像这样测试它时:co1==co2它工作得很好,但当我将它们放入数组并尝试使union时,它不会删除重复项。如果按照手册中的描述操作,它应该可以工作()并实现
-方法。我不知道如何定义其中一个对象是小于还是大于另一个。它们只是常规对象,可以具有其所有属性的equa值,也可以不具有equa值。我不知道这有什么帮助(实现)这是最好的解决方案。重写
eql?
hash
方法可能会很危险,因为对象的所有用户都会受到这些更改的影响。通过在本地定义块,可以确保不会无意中使用比较。