是否可以在Ruby的Set中使用自定义相等运算符?

时间:2019-07-18 02:31:38

标签: ruby-on-rails ruby set

我需要在2个父母之间比较子对象的集合。每个对象大约有30,000个对象,并具有大约十二种各种属性。 Ruby的Set类提供了一种快速方法,可以从另一个集合中减去一个集合,并获得差值。我一直在用JSON数据执行此操作,整个过程只花了几秒钟。

现在我正在使用ActiveRecord来获取数据集。当然,一旦将子级从数据库中解组出来,它们将包括属性:id:created_at:updated_at。不幸的是,这会自动破坏diff中的比较,因为这些字段将始终不同,从而导致比较失败。

在这组属性中,我实际上只关心:label:data。也就是说,我想比较两组中具有相同标签的对象,看它们的数据是否不同。

我可以在我的课程中添加一个自定义等效运算符:

def ==(other)
    self.label == other.label && self.data == other.data
end

这在单个对象的比较之间起作用。如果(只是)其标签和数据匹配,则认为它们相等。但是,出于确定等效性的目的,此操作似乎未使用此替代项:

@diff = (@left.to_set - @right.to_set)

我希望Set可以使用对象的类的重写==运算符,但是事实并非如此。我的差异只是一侧或另一侧的全部,具体取决于差异的顺序。有什么办法可以做到这一点? (我已经尝试覆盖.eql?。)


由于注释太长,因此这里是该想法的SQL实现。

WITH 
    t1 AS (SELECT * FROM tunings WHERE calibration_id = 7960),
    t2 AS (SELECT * FROM tunings WHERE calibration_id = 7965)
SELECT t1.label, t1."data", t2."data" FROM t1 FULL OUTER JOIN t2 ON t1.label = t2.label
WHERE t1."data" != t2."data" OR t1."data" IS NULL OR t2."data" IS NULL

我什至没有提到的另一个速度问题是,当我在视图中显示差异时,我必须从对应的设置中查找“正确”的值,而这又需要10秒钟。这一切都一步一步完成。

由于CTE的原因,我想我无法将其放入ActiveRecord语义中,我只需要将原始SQL与种子值一起传递,但是我希望证明自己是错误的。

此外,我仍然在学术上对原始问题感兴趣。

2 个答案:

答案 0 :(得分:3)

根据Ruby Set class: equality of sets,您需要同时覆盖Object#eql?Object#hash

答案 1 :(得分:0)

这是您在常规Ruby中无需重新定义类的身份的方法。

first = [{ id: 1, label: "foo", data: "foo"},
         { id: 2, label: "bar", data: "bar"},
         { id: 3, label: "baz", data: "baz"}]
second = [{ id: 1, label: "foo", data: "foo"},
          { id: 2, label: "baz", data: "baz"},
          { id: 3, label: "quux", data: "quux"}]

first_groups = first.group_by { |e| e.values_at(:label, :data) }
second_groups = second.group_by { |e| e.values_at(:label, :data) }

first_minus_second_keys = first_groups.keys.to_set - second_groups.keys.to_set
first_minus_second = first_minus_second_keys.flat_map { |k| first_groups[k] }

(这是针对哈希列表的;对于AR类,您将e.values(:label, :data)替换为[e.label, e.data]

也就是说,我同意“锡曼”的观点:在数据库级别执行此操作会更有效率。