【问题标题】:Ruby array sort without changing position of nilsRuby数组排序而不改变nils的位置
【发布时间】:2016-04-27 06:58:24
【问题描述】:

我正在处理一组数组。我需要对行进行排序,排序标准是从左到右依次为每列(从第 2 列开始)的值。 Nil 值必须被推到底部。一旦被推到底部,我需要保留 nil 列,因为它们没有连续的搜索改变它们相对于彼此的位置。从这个数组开始:

sort_array = 
[[297, 100, 101, 235, 253, nil, nil, nil],
 [286, 116, 116, 213, nil, nil, nil, nil],
 [256, 105, 111, 212, 216, 264, nil, nil],
 [276, 108, 111, 204, 207, 257, 259, 367],
 [274,  66,  66, 120, 121, nil, 150, nil],
 [298, 114, 117, 270, 270, nil, nil, nil],
 [296, 127, 130, 259, 264, 324, 332, nil],
 [283, 102, 106, 193, 199, 247, 248, 343]]

我用这些代码行遍历数组:

(1..sort_array[0].size - 1).each do |i|
  sort_array.sort_by! { |e| [e[i] ? 0 : 1, e[i]] }
end

这个想法是忽略第一列,但按升序对第二列到第 n 列进行排序,将 nil 值推到底部。这给出了以下结果:

  => [[283, 102, 106, 193, 199, 247, 248, 343],
     [276, 108, 111, 204, 207, 257, 259, 367],
     [256, 105, 111, 212, 216, 264, nil, nil],
     [297, 100, 101, 235, 253, nil, nil, nil],
     [296, 127, 130, 259, 264, 324, 332, nil],
     [274,  66,  66, 120, 121, nil, 150, nil],
     [298, 114, 117, 270, 270, nil, nil, nil],
     [286, 116, 116, 213, nil, nil, nil, nil]]

这与我需要的很接近,但并不完全正确。问题是一旦被推到底部的 nil 值就不会保持不变。例如,对第一个值为 247 的列进行排序后,数组将如下所示:

=> [[283, ... 247, ...]
    [276, ... 257, ...]
    [256, ... 264, ...]
    [296, ... 324, ...]
    [297, ... nil, ...]
    [274, ... nil, ...]
    [298, ... nil, ...]
    [286, ... nil, ...]

但是在右侧的列上进一步排序会导致 nil 值被重新排序,这会打乱排序。 Nil 值需要保持不变,除非右边的单元格不是 nil,在这种情况下,它应该像往常一样排序。

排序完成后表格应如下所示:

  => [[283, 102, 106, 193, 199, 247, 248, 343],
     [276, 108, 111, 204, 207, 257, 259, 367],
     [274,  66,  66, 120, 121, nil, 150, nil],
     [296, 127, 130, 259, 264, 324, 332, nil],
     [256, 105, 111, 212, 216, 264, nil, nil],
     [297, 100, 101, 235, 253, nil, nil, nil],
     [298, 114, 117, 270, 270, nil, nil, nil],
     [286, 116, 116, 213, nil, nil, nil, nil]]

有人可以帮我写一个可以达到这个结果的方法吗?

查看我正在努力实现的真实示例可能会有所帮助。这是电子表格的链接:

https://github.com/SplitTime/OpenSplitTime/blob/master/hardrock2015test.xlsx

每一行都是跑步者的努力。当然,跑步者是按最终完成时间排名的。但是那些没有完成的人会按照他们到达的距离以及到达最后一个航点的时间来排名。

虽然链接的电子表格没有显示它,但排序还需要处理带有右侧时间数据的 nil 值,表示由于任何原因未记录的时间。

【问题讨论】:

  • 不清楚你的意思。元素似乎没有改变它们的列。从左到右对每一列进行排序是什么意思?您只能从左到右对(两个或更多)进行排序。
  • 为什么从 274,666,666 开始的行没有进一步向下(因为倒数第二个元素为 nil)?
  • 你有什么问题?
  • 元素总是留在它们的列中。排序会影响整个行。但是排序标准首先应用于第 2 列,然后应用于第 3 列,依此类推到第 n 列。我将进行编辑以澄清。
  • 3 点范围表示法正好适合您使用- 1 的情况。你可以改用(1...sort_array[0].size).each... BTW,为什么你从 1 开始而不是 0?

标签: arrays ruby sorting


【解决方案1】:

认为您希望按倒序对行的倒数第二个元素进行排序(nil 被视为Infinity):

arrays = [[297, 100, 101, 235, 253, nil, nil, nil],
          [286, 116, 116, 213, nil, nil, nil, nil],
          [256, 105, 111, 212, 216, 264, nil, nil],
          [276, 108, 111, 204, 207, 257, 259, 367],
          [274,  66,  66, 120, 121, nil, 150, nil],
          [298, 114, 117, 270, 270, nil, nil, nil],
          [296, 127, 130, 259, 264, 324, 332, nil],
          [283, 102, 106, 193, 199, 247, 248, 343]]

arrays.sort_by { |a| a[1..-1].reverse.map { |e| e || Float::INFINITY } }
#=> [[283, 102, 106, 193, 199, 247, 248, 343],
#    [276, 108, 111, 204, 207, 257, 259, 367],
#    [274,  66,  66, 120, 121, nil, 150, nil],
#    [296, 127, 130, 259, 264, 324, 332, nil],
#    [256, 105, 111, 212, 216, 264, nil, nil],
#    [297, 100, 101, 235, 253, nil, nil, nil],
#    [298, 114, 117, 270, 270, nil, nil, nil],
#    [286, 116, 116, 213, nil, nil, nil, nil]]

至少,这会产生预期的结果。

由于第一行由键组成,您可以利用 Ruby 的 array decomposition 功能并使用 |_, *vs| vs.reverse... 而不是 |a| a[1..-1].reverse...。这里,_ 指的是(未使用的)键,*vs 收集剩余的元素。

【讨论】:

  • 这会在这种情况下产生所需的结果,但我正在寻找一种算法,在每种情况下都能产生所需的结果。
  • @moveson 你能举个反例吗?
  • 我的立场是正确的。您的代码可以立即完美运行。非常感谢!我还是不太明白它的作用。你能解释一下吗?
  • @moveson arrays.map { |a| a[1..-1].reverse.map { |e| e || Float::INFINITY } } 返回排序所依据的中间数组。
  • 我现在明白了!我没有意识到数组排序可以评估本身就是数组的元素。这太棒了。再次感谢您!
猜你喜欢
  • 2018-01-16
  • 1970-01-01
  • 2016-03-02
  • 1970-01-01
  • 1970-01-01
  • 2016-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多