Он сравнивает значения, используя их хэш и EQL?методы повышения эффективности.
https://ruby -doc.org / core-2.5.0 / Array.html # method-i-uniq-3F
Поэтому вы должны переопределить eql?
(, то есть ==
) и hash
ОБНОВЛЕНИЕ:
Я не могу полностью объяснить, почему это так, но переопределяю hash
и ==
не работает.Я полагаю, это вызвано тем, что uniq
реализовано в C:
From: array.c (метод C): Владелец: Array Видимость: public Количество строк: 20
static VALUE
rb_ary_uniq(VALUE ary)
{
VALUE hash, uniq;
if (RARRAY_LEN(ary) <= 1)
return rb_ary_dup(ary);
if (rb_block_given_p()) {
hash = ary_make_hash_by(ary);
uniq = rb_hash_values(hash);
}
else {
hash = ary_make_hash(ary);
uniq = rb_hash_values(hash);
}
RBASIC_SET_CLASS(uniq, rb_obj_class(ary));
ary_recycle_hash(hash);
return uniq;
}
Вы можете обойти это, используя блочную версию uniq:
> [Foo.new(1,2), Foo.new(1,2), Foo.new(2,3)].uniq{|f| [f.a, f.b]}
=> [#<Foo:0x0000562e48937cc8 @a=1, @b=2>, #<Foo:0x0000562e48937c78 @a=2, @b=3>]
или использовать Struct
вместо:
F = Struct.new(:a, :b)
[F.new(1,2), F.new(1,2), F.new(2,3)].uniq
# => [#<struct F a=1, b=2>, #<struct F a=2, b=3>]
UPDATE2:
Фактически в терминахпереопределение это не то же самое, если вы переопределите ==
или eql?
.Когда я переопределил eql?
Он работал как задумано:
class Foo
attr_accessor :a, :b
def initialize(a, b)
@a = a
@b = b
end
def eql?(other)
(@a == other.a && @b == other.b)
end
def hash
[a, b].hash
end
def to_s
"#{@a}: #{@b}"
end
end
a = [
Foo.new(1, 1),
Foo.new(1, 2),
Foo.new(2, 1),
Foo.new(2, 2),
Foo.new(2, 2)
]
a.uniq
#=> [#<Foo:0x0000562e483bff70 @a=1, @b=1>,
#<Foo:0x0000562e483bff48 @a=1, @b=2>,
#<Foo:0x0000562e483bff20 @a=2, @b=1>,
#<Foo:0x0000562e483bfef8 @a=2, @b=2>]