我一直在尝试找出如何在多个条件下使用max_by
和max
方法。虽然我以为是,但经过大量测试,事实并非如此。
例如,我有一个散列,其值是散列:
chapters = { "686050": {
"volume": '1',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_434_815
},
"686049": {
"volume": '2',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_300_815
},
"686048": {
"volume": '2',
"chapter": '2',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_300_815
}
}
我想找到一个散列最大的AND散列。因此,在这种情况下,我希望获得第2卷第2章的内容。
我的第一个实现是使用max
:
chapters.max do |a, b|
a[1][:volume].to_d <=> b[1][:volume].to_d &&
a[1][:chapter].to_d <=> b[1][:chapter].to_d
end
查看此内容时,我的想法是,它将首先按数量进行比较,然后按章节进行比较。这段代码一直有效...直到我将哈希更改为:
"686050": {
"volume": '1',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_434_815
},
"686049": {
"volume": '2',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_300_815
}
哪个突然开始归还第一卷和第二章!我仍然不确定为什么会这样...
因此,我决定在相同的假设下,尝试使用max_by
来使用chapters.max_by { |_, v| v[:volume].to_d && v[:chapter].to_d }
。当每卷只有一个章节时,也会失败。
我的解决方案最终是:
chapters
.group_by { |_, v| v[:volume].to_d }.max.last.to_h
.max_by { |_, v| v[:chapter].to_d }
这是有明显原因的,但感觉效率很低。必须有一种使用单个max
或max_by
的方法。很想听听关于此的一些想法
答案 0 :(得分:1)
您可以执行以下操作。
def max_val(h,key)
h.map { |_,g| g[key] }.max
end
max_volume = max_val(h, :volume) #=> "2"
max_chapter = max_val(h, :chapter) #=> "2"
chapters.find { |_,g| g[:volume] == max_volume && g[:chapter] == max_chapter }
#=> [:"686048", {:volume=>"2", :chapter=>"2", :title=>"Chapter Title",
# :lang_code=>"gb", :timestamp=>1565300815}]
如果没有值包含两个键的最大值,则返回 nil
。如果要返回具有所需属性的所有键值对(即,如果有联系),请用find
替换select
。如果没有值包含两个键的最大值,则该数组将为空。
这需要三遍散列。可以一次完成该操作,但很麻烦,我希望它会更耗时,主要是因为Enumerable#map
和Array#max
都是用C实现的。
可以通过以下步骤一次获得所需的结果。
max_volume = 0.chr
max_chapter = 0.chr
candidate = [nil, {}]
chapters.each do |k,g|
old_max_volume = max_volume
max_volume = [max_volume, g[:volume]].max
old_max_chapter = max_chapter
max_chapter = [max_chapter, g[:chapter]].max
if max_volume > old_max_volume || max_chapter > old_max_chapter
candidate = (max_volume == g[:volume] && max_chapter == g[:chapter]) ?
[k,g] : [nil, {}]
end
end
candidate
#=> [:"686048", {:volume=>"2", :chapter=>"2", :title=>"Chapter Title",
# :lang_code=>"gb", :timestamp=>1565300815}]
现在设置:
chapters[:"686048"][:volume] = '1'
上面的第一个方法现在返回nil
,第二个(单次通过)返回[nil, {}]
答案 1 :(得分:0)
# example 1
a = %w[albatross dog horse]
a.max_by(2) {|x| x.length } #=> ["albatross", "horse"]
# example 2 - Arrays are compared in an “element-wise” manner
arrays = [['1', '1'], ['2', '1'], ['2', '2']]
arrays.max_by(2) { |arr| arr } #=> ["2","2"], ["2", "1"]
所以
chapters.max_by { |_, elem| [elem[:volume], elem[:chapter]] }
让我们测试
book1 = {
"686050": {
"volume": '1',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_434_815
},
"686049": {
"volume": '2',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_300_815
},
"686048": {
"volume": '2',
"chapter": '2',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_300_815
}
}
book2 = {
"686050": {
"volume": '1',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_434_815
},
"686049": {
"volume": '2',
"chapter": '1',
"title": 'Chapter Title',
"lang_code": 'gb',
"timestamp": 1_565_300_815
}
}
pp book1.max_by(2) { |_, elem| [elem[:volume], elem[:chapter]] }
pp book2.max_by { |_, elem| [elem[:volume], elem[:chapter]] }
输出
# 1st test
[[:"686048",
{:volume=>"2",
:chapter=>"2",
:title=>"Chapter Title",
:lang_code=>"gb",
:timestamp=>1565300815}],
[:"686049",
{:volume=>"2",
:chapter=>"1",
:title=>"Chapter Title",
:lang_code=>"gb",
:timestamp=>1565300815}]]
# 2nd test
[:"686049",
{:volume=>"2",
:chapter=>"1",
:title=>"Chapter Title",
:lang_code=>"gb",
:timestamp=>1565300815}]