如何比较关于元素顺序的两个多维哈希
How to compare two multidimensional hashes regarding to elements order
我陷入了这样的问题。我有 2 个哈希值,我试图在 rspec 测试中进行比较:
describe 'sort tests' do
let(:actual) do
{
1 => { users: { 1 => { id: 1,
accounts: [1],
profit: 10,
revenue: 1,
loss: 9 },
2 => { id: 2,
accounts: [2, 3, 6],
profit: -24,
revenue: 6,
loss: -30 } },
total_profit: -14,
total_revenue: 7,
total_loss: -21 },
2 => { users: { 3 => { id: 3,
accounts: [4, 5],
profit: 27,
revenue: 9,
loss: 18 } },
total_profit: 27,
total_revenue: 9,
total_loss: 18 }
}
end
let(:expected) do
{
1 => { users: { 2 => { id: 2,
accounts: [2, 3, 6],
profit: -24,
revenue: 6,
loss: -30 },
1 => { id: 1,
accounts: [1],
profit: 10,
revenue: 1,
loss: 9 } },
total_profit: -14,
total_revenue: 7,
total_loss: -21 },
2 => { users: { 3 => { id: 3,
accounts: [4, 5],
profit: 27,
revenue: 9,
loss: 18 } },
total_profit: 27,
total_revenue: 9,
total_loss: 18 }
}
end
it 'sort' do
def mysort(data)
data.each do |_, partner|
partner[:users].sort_by { |_k, user| user[:loss] }.to_h
partner
end
data.sort_by { |_, partner| partner[:total_loss] }.to_h
end
expect(mysort(actual)).to eql expected
# expect(Digest::MD5.hexdigest(Marshal::dump(mysort(actual)))).to eql Digest::MD5.hexdigest(Marshal::dump(expected))
end
end
测试通过。但是,如果我取消注释 md5 检查,它会引发哈希不同的错误:
expected: "155d27d209f286fb1fc9ebeb0dcd6d3d"
got: "255df98d4fc8166d0d8ffc7227ffd351"
所以,eql 实际上并没有正确地比较散列,而且 mysort 函数中有一个错误:
def mysort(data)
data.each do |_, partner|
partner[:users] = partner[:users].sort_by { |_k, user| user[:loss] }.to_h
partner
end
data.sort_by { |_, partner| partner[:total_loss] }.to_h
end
现在可以排序了,但是只有比较 md5 校验和才能帮助理解哈希值是否相等:(
如何在没有这个 hack 的情况下正确比较哈希值?
我会使用这样的东西:
compare = proc do |a, b|
next a == b unless a.is_a?(Hash) && b.is_a?(Hash)
next false unless a.size == b.size
a.keys.zip(b.keys).all?(compare) && a.values.zip(b.values).all?(compare)
end
在上述情况下,您无法将 proc
换成 lambda
。原因是 proc 隐式地进行数组分解而 lambda 没有。 (你可以使用 compare.([a, b])
而不会破坏它,而使用 lambda 时你不能这样做。)
我个人是保护子句的粉丝,主要是因为我发现它们比一个大表达式更清晰。
此解决方案检查顺序和值的顺序。
compare.({{a: 1, b: 2} => 1}, {{b: 2, a: 1} => 1}) #=> false
compare.({a: {b: 2, c: 3}}, {a: {c: 3, b: 2}}) #=> false
compare.({a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}) #=> true
ps。如果您使用的是 2.5.0 以下的 Ruby 版本,则必须改用 .all?(&compare)
(注意 &
)。
我陷入了这样的问题。我有 2 个哈希值,我试图在 rspec 测试中进行比较:
describe 'sort tests' do
let(:actual) do
{
1 => { users: { 1 => { id: 1,
accounts: [1],
profit: 10,
revenue: 1,
loss: 9 },
2 => { id: 2,
accounts: [2, 3, 6],
profit: -24,
revenue: 6,
loss: -30 } },
total_profit: -14,
total_revenue: 7,
total_loss: -21 },
2 => { users: { 3 => { id: 3,
accounts: [4, 5],
profit: 27,
revenue: 9,
loss: 18 } },
total_profit: 27,
total_revenue: 9,
total_loss: 18 }
}
end
let(:expected) do
{
1 => { users: { 2 => { id: 2,
accounts: [2, 3, 6],
profit: -24,
revenue: 6,
loss: -30 },
1 => { id: 1,
accounts: [1],
profit: 10,
revenue: 1,
loss: 9 } },
total_profit: -14,
total_revenue: 7,
total_loss: -21 },
2 => { users: { 3 => { id: 3,
accounts: [4, 5],
profit: 27,
revenue: 9,
loss: 18 } },
total_profit: 27,
total_revenue: 9,
total_loss: 18 }
}
end
it 'sort' do
def mysort(data)
data.each do |_, partner|
partner[:users].sort_by { |_k, user| user[:loss] }.to_h
partner
end
data.sort_by { |_, partner| partner[:total_loss] }.to_h
end
expect(mysort(actual)).to eql expected
# expect(Digest::MD5.hexdigest(Marshal::dump(mysort(actual)))).to eql Digest::MD5.hexdigest(Marshal::dump(expected))
end
end
测试通过。但是,如果我取消注释 md5 检查,它会引发哈希不同的错误:
expected: "155d27d209f286fb1fc9ebeb0dcd6d3d"
got: "255df98d4fc8166d0d8ffc7227ffd351"
所以,eql 实际上并没有正确地比较散列,而且 mysort 函数中有一个错误:
def mysort(data)
data.each do |_, partner|
partner[:users] = partner[:users].sort_by { |_k, user| user[:loss] }.to_h
partner
end
data.sort_by { |_, partner| partner[:total_loss] }.to_h
end
现在可以排序了,但是只有比较 md5 校验和才能帮助理解哈希值是否相等:(
如何在没有这个 hack 的情况下正确比较哈希值?
我会使用这样的东西:
compare = proc do |a, b|
next a == b unless a.is_a?(Hash) && b.is_a?(Hash)
next false unless a.size == b.size
a.keys.zip(b.keys).all?(compare) && a.values.zip(b.values).all?(compare)
end
在上述情况下,您无法将 proc
换成 lambda
。原因是 proc 隐式地进行数组分解而 lambda 没有。 (你可以使用 compare.([a, b])
而不会破坏它,而使用 lambda 时你不能这样做。)
我个人是保护子句的粉丝,主要是因为我发现它们比一个大表达式更清晰。
此解决方案检查顺序和值的顺序。
compare.({{a: 1, b: 2} => 1}, {{b: 2, a: 1} => 1}) #=> false
compare.({a: {b: 2, c: 3}}, {a: {c: 3, b: 2}}) #=> false
compare.({a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}) #=> true
ps。如果您使用的是 2.5.0 以下的 Ruby 版本,则必须改用 .all?(&compare)
(注意 &
)。