ruby - 'group_by' 使用的 Ruby 比较
问题描述
我想对实际上相同但不相同的对象进行分组,例如在这种情况下:
class MyClass
attr_accessor :value
def initialize(value)
@value = value
end
def ==(other)
(@value - other.value).abs < 0.0001
end
end
使用与我的实现相关的精度,可以认为相差 0.0001 的两个值是相同的:
MyClass.new(1.0) == MyClass.new(1.00001)
# => true
我希望这些在同一个组中:
[MyClass.new(1.0), MyClass.new(1.00001)].group_by(&:value)
# => {1.0=>[#<MyClass:0x0000000d1183e0 @value=1.0>], 1.00001=>[#<MyClass:0x0000000d118390 @value=1.00001>]}
用于什么比较group_by
?是否可以制作内置group_by
以尊重自定义==
方法,或者是否需要自定义group_by
方法?
解决方案
TL;DR 在我看来,这个问题是因为 group_by 实际上并没有在任何地方检查相等性。它生成哈希,并使用数组的元素作为键。
很长的故事:
我在这里的第一个猜测是它正在做类似的事情my_arr.map(&:value).group_by { |i| i }
,这意味着它将检查 2 个浮点数而不是 2 个 MyClasses 的相等性。为了测试这一点,我重新定义==
了一个浮点数,并在我们的两个定义中添加了调试 puts 语句==
。有趣的是,没有打印任何内容。因此,我继续查看的文档group_by
,并查看了源代码:
static VALUE
enum_group_by(VALUE obj)
{
VALUE hash;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
hash = rb_hash_new();
rb_block_call(obj, id_each, 0, 0, group_by_i, hash);
OBJ_INFECT(hash, obj);
return hash;
}
注意最后一个参数rb_block_call
-- 它是一个哈希。这向我暗示,在引擎盖下,红宝石正在这样做:
def group_by(&:block)
h = {}
self.each do |ele|
key = block_given? ? block.call(ele) : ele
h[key] ||= []
h[key].push(ele)
end
end
从哈希中获取密钥时,似乎==
没有调用它,因此重新定义的尝试==
没有达到您想要的效果。解决此问题的方法如下:
[MyClass.new(1.0), MyClass.new(1.00001)].group_by { |i| i.value.round(2) }
推荐阅读
- node.js - 如何从特定端口读取和写入数据到控制台?
- python - 对字典/json文件中的值进行排序
- java - 在@AfterTest 方法中进行检查的正确策略
- reactjs - NPM 启动时没有 package.json 文件
- c# - Oracle.ManagedDataAccess 是按应用程序还是按服务器池化?
- javascript - ParentNode.replaceChildren 的替代方案
- aws-lambda - Akka.net Context.System.EventStream 在 aws lambda
- laravel - 这段代码是否干净(使用策略、资源、表单验证)
- sql - 如何检查 2 个表是否包含 SQL 中的相同列?
- python - 在 Spacy 3 中向管道添加自定义组件