【问题标题】:Ruby Sort an array of arrays of numbers based on multiple conditionsRuby 根据多个条件对数字数组的数组进行排序
【发布时间】:2016-09-04 20:38:13
【问题描述】:

我的数组如下所示:

to_sort = [[1, 27, -3, 1.0], [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0],
           [5, 27, -2, 5.0], [6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0],
           [9, 27, 2, 14.0]]

我想根据它们的第二个和第三个值按升序对这些数组进行排序,但是第三个数字为负数的数组必须按降序排序并放在其他数组之后。
结果应该是这样的:

sorted = [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0],
          [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0],
          [1, 27, -3, 1.0]]

如何做到尽可能优化?

【问题讨论】:

    标签: arrays ruby sorting


    【解决方案1】:

    这是一个三步法,但我确信还有另一个更简洁的答案。

    首先我们对第三个元素为正和/或零的值进行排序:

    pos = to_sort.select { |arr| arr[2] >= 0 }.sort_by { |arr| [arr[2], arr[3]] }
    => [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0]]
    

    然后我们对第三个元素为负的值进行排序:

    neg = to_sort.select { |arr| arr[2] < 0 }.sort_by { |arr| [-arr[2], arr[3]] }
    

    然后我们将它们组合在一起:

    pos + neg
    => [[6, 27, 1, 11.0],
     [7, 27, 1, 12.0],
     [8, 27, 1, 13.0],
     [9, 27, 2, 14.0],
     [2, 27, -2, 2.0],
     [3, 27, -2, 3.0],
     [4, 27, -2, 4.0],
     [5, 27, -2, 5.0],
     [1, 27, -3, 1.0]]
    

    【讨论】:

    • sort_by { |arr| [arr[2], arr[3]] 你能解释一下它是如何工作的吗,你应该首先在 arr[2] 之后进行排序,然后在 arr[2] 之后进行排序,然后在 arr[3] 之后进行排序...... ..+1
    【解决方案2】:

    我的理解是当a[2] &gt;= 0时,排序是在数组[a[1], a[2]]上进行,而a[2] &lt; 0的元素要在排序后的数组的末尾,并按[-a[1], -a[2]]排序。

    biggest_plus_1 = to_sort.map { |a| a[2] }.max + 1
      #=> 3
    to_sort.sort_by { |a| a[2] >= 0 ? [0, a[1], a[2]] : [biggest_plus_1, -a[1], -a[2]] }
      #=> [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0],
      #    [5, 27, -2, 5.0], [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0],
      #    [1, 27, -3, 1.0]] 
    

    Array#sortEnumerable#sort_by 依赖于方法 Array#<=> 来确定每对被排序的数组的顺序。两个数组ab 按字典顺序排列,含义如下。如果a[0] &lt; b[0]a 小于b (a &lt; b),或等价于a &lt;=&gt; b #=&gt; -1。同样,如果a[0] &gt; b[0]a 大于b (a &gt; b) 和a &lt;=&gt; b #=&gt; 1。如果a[0] == b[0],则通过以相同方式比较第二个元素来打破平局,依此类推。如果a 小于b (a.size &lt; b.size),并且每个数组的第一个a.size 元素相等,则a &lt; bab 当且仅当 a &lt;=&gt; b #=&gt; 0 相等。

    由于a 的元素a[2] &lt; 0 将被放置在已排序数组的末尾,因此我们需要按第一个元素将数组放置在已排序数组的前面或后面的数组进行排序。正是出于这个原因,我在a[2] &gt;= 0biggest_plus_1a[2] &lt; 0 时将排序数组的第一个元素设为零,其中biggest_plus_1a[2] 的最大值加1。

    排序依据数组的剩余元素确定两组数组中的每一个如何排序。

    请注意,如果所有a[2] &lt; 0 都为biggest_plus_1,则biggest_plus_1 将是非正数,但这没关系,因为没有元素将按第一个元素为零的数组排序。

    【讨论】:

    • 非常感谢,它工作得很好(a[1] > 0,很抱歉这个错误)。你能解释一下它是如何工作的,或者给我一个解释语法如何工作的链接吗?
    【解决方案3】:

    我对你问题的看法是:

    to_sort.sort_by { |a| a[1].abs; a[2] < 0 ? a[2].abs+1 : a[2] }
    #=>[[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0],
    # [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0],
    # [1, 27, -3, 1.0]]
    

    另外,我们可以只使用.abs,它看起来像:

    to_sort.sort_by { |a| a[1].abs; a[2].abs } 
    

    但是这样-2 == 2返回true,结果会是这样的:

    #=> [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [2, 27, -2, 2.0],
    # [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0], [9, 27, 2, 14.0],
    # [1, 27, -3, 1.0]]
    

    【讨论】:

      【解决方案4】:

      排序依据创建和数组并使用它对值进行排序。 我们需要做的就是创建一个遵循所需逻辑的数组。顺序:

      to_sort.sort_by do |array|
        [
          array[2] > 0 ? -1 : 1,  # Put all non-negative numbers of ix 2 first.
          array[2].abs,           # Sort by absolute value of ix 2.
          array[3]                # Then sort using ix 3.
        ]
      }
      

      结果:

      #=> [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0], [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0], [1, 27, -3, 1.0]]
      

      【讨论】:

        猜你喜欢
        • 2012-02-11
        • 1970-01-01
        • 2013-09-30
        • 2012-12-01
        • 1970-01-01
        • 2014-09-21
        • 2013-11-10
        • 2021-03-08
        • 2018-08-16
        相关资源
        最近更新 更多