Ruby on rails 在多个条件下使用max_by的有效方法

Ruby on rails 在多个条件下使用max_by的有效方法,ruby-on-rails,ruby,sorting,Ruby On Rails,Ruby,Sorting,我一直在想如何在多种条件下使用max\u by和max方法。虽然我认为我做到了,但经过广泛的测试,结果证明并非如此 例如,我有一个值为哈希的哈希: chapters = { "686050": { "volume": '1', "chapter": '1', "title": 'Chapter Title', "lang_code": 'gb', "timestamp": 1_565_434_815 },

我一直在想如何在多种条件下使用
max\u 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
      }
    }
我想找到一个散列,它有最大的卷和章节。所以在本例中,我希望得到第二卷和第二章的内容

我的第一个实现是使用
max

chapters.max do | a,b|
a[1][:卷].至\u d b[1][:卷].至\u d&&
a[1][:章].至d b[1][:章].至d
结束
当我看到这一点时,我的想法是,它将首先按体积进行比较,然后按章节进行比较。这个代码已经起作用了。。。直到我将哈希更改为:

“686050”:{
“卷”:“1”,
“第章”:“1”,
“标题”:“章节标题”,
“语言代码”:“gb”,
“时间戳”:1_565_434_815
},
"686049": {
“卷”:“2”,
“第章”:“1”,
“标题”:“章节标题”,
“语言代码”:“gb”,
“时间戳”:1_565_300_815
}
它突然开始返回第一卷和第二章!我仍然不确定为什么会发生这种情况

所以我决定尝试
max_by
,使用这个
chapters.max_by{| |,v | v[:volume].to|d&&v[:chapter].to|d}
,使用相同的假设。当每卷只有一章时,它也以失败告终

我的解决方案是:

章节
.group_by{|,v | v[:volume].to|d}.max.last.to|h
.max_by{|,v | v[:章].to|d}

这样做的原因很明显,但感觉效率很低。必须有一种方法使用单个
max
max\u by
。希望听到一些关于这方面的想法

您可以执行以下操作

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
a=%w[信天翁狗马]
a、 max|u by(2){| x | x.length}#=>[“信天翁”,“马”]
#示例2-以“元素方式”比较数组
数组=['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": {
“卷”:“1”,
“第章”:“1”,
“标题”:“章节标题”,
“语言代码”:“gb”,
“时间戳”:1_565_434_815
},
"686049": {
“卷”:“2”,
“第章”:“1”,
“标题”:“章节标题”,
“语言代码”:“gb”,
“时间戳”:1_565_300_815
},
"686048": {
“卷”:“2”,
“章节”:“2”,
“标题”:“章节标题”,
“语言代码”:“gb”,
“时间戳”:1_565_300_815
}
}
第二册={
"686050": {
“卷”:“1”,
“第章”:“1”,
“标题”:“章节标题”,
“语言代码”:“gb”,
“时间戳”:1_565_434_815
},
"686049": {
“卷”:“2”,
“第章”:“1”,
“标题”:“章节标题”,
“语言代码”:“gb”,
“时间戳”: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}]

我相信你不会反对我的编辑。我主要添加了变量
h
来保存你的散列,这样读者就可以在答案和评论中引用该变量,而无需对其进行定义。一点也不@CarySwoveland,尽管我会将其编辑为章节,因为这是我使用的,也是我的代码块中引用的。我相信你的代码有问题。请看我对@calvin答案的评论。如果您按照我的建议在修改
book2
后设置
chapters=book2
,您的代码返回
[:“686049”、{:volume=>“2”、:chapter=>“1”、:title=>“chapter title”、:lang_code=>“gb”、:timestamp=>1565300815}]
,我认为这不符合您的要求。假设您的
book2
更改如下:
book2[:“686050”][:chapter]=“2”
。然后
book2.max\u通过{| |,elem{elem[:volume],elem[:chapter]}=>[:“686049”,{:volume=>“2”,…]
,但这是不正确的,因为
h
的任何值都不包含
:volume
的最大值和
:chapter
的最大值。是的,也有绑定的书籍。因此,在此之前有一个步骤,即按时间戳对书籍进行排序,以便max_按选择第一个,即最新上载的。是的,这一关看起来一团糟。对我来说可读性比性能更重要,所以你的第一个建议很好。不过我可能会坚持我的建议,因为它更简洁,即使还有几步