【问题标题】:Ruby - Compare array element values against other elementsRuby - 将数组元素值与其他元素进行比较
【发布时间】:2014-11-21 00:05:45
【问题描述】:

假设我有一个对象(或哈希)数组,如下所示 -

a = [ {salary: 10, products: 1}, {salary: 9, products: 1}, {salary: 10, products: 0} ]

如果数组中存在另一个元素在相同或更少的薪水下具有更高的产品值,我想从数组中删除元素。覆盖这两个实例的一些伪逻辑看起来像这样-

IF EXISTS 'other element' WITH >= products and < salary THEN DELETE

IF EXISTS 'other element' WITH > products and =< salary THEN DELETE

哪些 Ruby 方法可以帮助我完成此任务?

编辑:在上面的例子中,预期的输出是{salary: 9, products: 1}。应该删除第一个元素,因为还有另一个元素的薪水较低,但产品相同(或更高)。可以删除第三个元素,因为第一个元素具有相同薪水的更多产品,或者因为第二个元素具有更少薪水的更多产品。

【问题讨论】:

  • Array#max_by 和 Array#delete_if 或 Array#reject
  • 我知道这些方法,但是如何将它们与其他元素进行比较?我正在想办法使用selectinclude 来引用块内的数组,看看是否存在这种情况。
  • 只要使用delete_if或者reject...如果你知道这些方法,那我们为什么还要讨论这个呢?这是一个班轮。
  • 您不需要将它们与其他元素进行比较,只需找到您要测试的值,然后使用它来构建 Array#reject 的谓词。
  • @IronSavior 对每个元素进行不同测试的值不是吗?这就是我理解问题的方式。

标签: ruby arrays


【解决方案1】:

首先,另一种方法

这可以通过对问题进行一点分解来解决。您要使用的机制的描述使我相信您实际上是在尝试按产品:薪水的比率对元素进行排序。您可以通过像这样对它们进行排序(a 是哈希数组)一次性识别出这些表现最佳的人:

sorted = a.sort_by{|h| Rational h[:products], h[:salary] }

从那里,您可以随意浏览数组sorted 的顶部或底部。这可能会告诉您您想要了解的有关数据的内容。

但万一这不是你真正想要的......

描述的解决方案

如果您真的希望按照您描述的方式计算它,那么您将不得不对集合进行几次传递。我可能会使用以下函数表达式。

selected = a.map{ |h|
  h[:salary]
}.uniq.map{ |salary|
  a.select{ |h|
    h[:salary] <= salary
  }.max_by{ |h|
    h[:products]
  }
}.uniq

如果您有一个非常大的输入集,您可以优化掉select 块以获得一些性能提升,但它不会那么容易阅读。您必须决定这对您是否重要。

请注意,这只会为每个薪水范围选择一个元素,因此如果您有两个元素具有完全相同的薪水和产品,最终列表中只会出现一个。如果你想包含绑定的元素,那么它只是稍微复杂一点:

selected = a.map{ |h|
  h[:salary]
}.uniq.flat_map{ |salary|
  most = 0
  a.select{ |h|
    h[:salary] <= salary
  }.tap{ |group|
    most = group.map{|h| h[:products] }.max
  }.select{ |h|
    h[:products] == most
  }
}.uniq

【讨论】:

  • 您的代码返回[{:salary=&gt;9, :products=&gt;1}],我认为这不正确。
  • @CarySwoveland 是正确的。再次查看给定的数据。所有其他的薪水都更高。
  • 如果有另一个元素具有更高的产品价值,则该元素将被拒绝......我们可以停在那里。没有要拒绝 h[products' =&gt; 1 的元素 h,因为没有任何元素的乘积值大于 1
  • @CarySwoveland The questioner says 输出应该是[{:salary=&gt;9, :products=&gt;1}]
  • 是的,我看到了那个编辑。这个问题是矛盾的。真是一团糟!
【解决方案2】:

这行得通,但它并不优雅或高效:

a.reject { |e1| a.any? { |e2| e1 != e2 && e2[:salary] <= e1[:salary] && e2[:products] >= e1[:products] } }

问题在于它是 O(n^2) - 对于数组中的每个元素,它会再次遍历数组。如果效率对您来说不是一个大问题,那么这可能是最清楚的开始。

唯一想到的其他解决方案涉及将数组转换为树或其他数据结构以提高效率,但这看起来和感觉起来要复杂得多。

【讨论】:

  • 这实际上最接近我所寻找的。出于某种原因,它并没有给我我所知道的正确的期望输出。我的实际程序使用了一组对象数组,但为了解释起见,我在示例中对其进行了简化。这就是我现在正在尝试的a = a.delete_if { |e1| a.any? { |e2| e1 != e2 &amp;&amp; e2.sum(&amp;:salary) &lt;= e1.sum(&amp;:salary) &amp;&amp; e2.sum(&amp;:score) &gt;= e1.sum(&amp;:score) } },这个逻辑在一个数组上运行了几次,因为该数组迭代地添加了更多的元素。不过,删除时似乎没有完全考虑到score
  • 单行代码的问题是它们很难调试!看起来该代码与我发布的代码完全相同,因此它应该可以工作。如果您使用对象发布一些测试数据,我可以看看,否则将很难从这里做更多的事情。
  • 戴夫,我发现了问题。我的内部数组可以相互重复,因此它们相互匹配,因为逻辑包含“或等于”。我只是将分数标准设置为大于避免这种情况,以及在运行此单行之前删除重复项。我也将&amp;&amp; 切换到and,这似乎有助于解决其他一些错误问题。最终结果-a.reject! { |e1| a.any? { |e2| e1 != e2 and e2.sum(&amp;:salary) &lt;= e1.sum(&amp;:salary) and e2.sum(&amp;:score) &gt; e1.sum(&amp;:score) } }感谢您的帮助!
  • 太棒了!我唯一担心的是and 真的不等同于&amp;&amp; 所以这不应该是真的有效。但只要它对你有用,我就很高兴。
【解决方案3】:

一种方式:

a = [ {salary: 10, products: 1},
      {salary:  9, products: 1},
      {salary: 12, products: 0},
      {salary: 10, products: 0} ]

b = a.group_by {|h| h[:salary]}.values.map {|a| a.max_by { |h| h[:products]}}
  #=> [{:salary=>10, :products=>1},
  #    {:salary=>9, :products=>1},
  #    {:salary=>12, :products=>0}]
a.reject {|h| b.any? {|g|
  (g[:salary] <= h[:salary]) && g[:products] > h[:products]}}
  #=> [{:salary=>10, :products=>1}, {:salary=>9, :products=>1}]

b 实际上是“不受约束的”哈希;即,唯一需要在下一步中考虑的。可以跳过这一步,但如果未支配的哈希比例不太大,会提高效率。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 2013-06-06
    • 2021-06-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多