将排序后的Ruby数组转换为可能重复的列组

将排序后的Ruby数组转换为可能重复的列组,ruby,arrays,Ruby,Arrays,我在Ruby中有以下数字数组(越高越好),我想对它们进行排序。换句话说,我要转换以下已排序列表: [89 52 52 36 18 18 5] 以下职级: [1 2 2 4 5 5 7] 例如,获胜者获得第一名,第二名有平局,等等。很明显,重要的一点是联系是可能的,而这些联系必须跳过相应的等级。任何数量的联系都是可能的(3人共享第二名) 有没有一种优雅的方式来执行这种操作?使用: 更新 rank, i = 1, 0 a.map { |x| i += 1 x != a[i-2] ? ra

我在Ruby中有以下数字数组(越高越好),我想对它们进行排序。换句话说,我要转换以下已排序列表:

[89 52 52 36 18 18 5]
以下职级:

[1 2 2 4 5 5 7]
例如,获胜者获得第一名,第二名有平局,等等。很明显,重要的一点是联系是可能的,而这些联系必须跳过相应的等级。任何数量的联系都是可能的(3人共享第二名)

有没有一种优雅的方式来执行这种操作?

使用:

更新

rank, i = 1, 0
a.map { |x|
  i += 1
  x != a[i-2] ? rank = i : rank
}
# => [1, 2, 2, 4, 5, 5, 7]

我不知道什么是“优雅”,但这里有一个简短、易读、超级直截了当的解决方案:

# assume a pre-sorted non-sparse array
arr = [89, 52, 52, 36, 18, 18, 18, 18, 7]

run = rank = 0
last_n = nil

ranked = arr.map do |n|
  run += 1
  next rank if n == last_n
  last_n = n
  rank += run
  run = 0
  rank
end

p ranked
# => [1, 2, 2, 4, 5, 5, 5, 5, 9]
基准 我想我们正在做这个

编辑:这篇文章太长了,所以我将基准代码移至要点:

结果如下:

falsetru 730.9(±2.1%)i/s-3672英寸5.025755秒
falsetru(2)1289.9(±2.7%)i/s-6500英寸5.042749秒
sawa 986.9(±2.1%)i/s-5000英寸5.068450秒
约旦1644.9(±1.9%)i/s-8250英寸5.017334英寸
冰人6.4(±0.0%)i/s-32英寸5.035015s
西蒙加尼耶1053.9(±1.9%)i/s-5304英寸5.034452英寸
Cary Swoveland 511.4(±3.5%)i/s-2600英寸5.090605秒
编辑:我这里有一些关于的东西,但结果证明我用错了。实际上,它在任何情况下都没有提高性能

a = [89, 52, 52, 36, 18, 18, 5]
a.map{ |e| a.index(e) + 1 }
# => [1, 2, 2, 4, 5, 5, 7]
编辑:

来自@Jordan gist()的基准

用于避免每次迭代都遍历数组:

a = [89, 52, 52, 36, 18, 18, 5]
rank = 1
a.each_with_index.map{|value, i| a[i-1] == value ? rank : rank = i+1}
编辑:我想我也应该基准我的,这里是结果

Calculating -------------------------------------
            falsetru       344 i/100ms
                sawa       436 i/100ms
              Jordan      1174 i/100ms
              Iceman        46 i/100ms
        simongarnier       710 i/100ms
-------------------------------------------------
            falsetru     3411.0 (±3.7%) i/s -      17200 in   5.049500s
                sawa     4437.4 (±3.4%) i/s -      22236 in   5.017073s
              Jordan    11746.5 (±2.3%) i/s -      59874 in   5.099797s
              Iceman      463.4 (±2.4%) i/s -       2346 in   5.065717s
        simongarnier     7442.1 (±3.2%) i/s -      37630 in   5.061725s
虽然不是最好的,但还是打得很好

另一种方式,即使用

代码

def ranks(a)
  a.each_with_index
   .chunk { |e,_| e }
   .flat_map { |_,a| [a.first.last+1]*a.size }
end
a = [89, 52, 52, 36, 18, 18, 5]

ranks(a)
  #=> [1, 2, 2, 4, 5, 5, 7]
示例

def ranks(a)
  a.each_with_index
   .chunk { |e,_| e }
   .flat_map { |_,a| [a.first.last+1]*a.size }
end
a = [89, 52, 52, 36, 18, 18, 5]

ranks(a)
  #=> [1, 2, 2, 4, 5, 5, 7]
解释

对于上面的阵列
a

b = a.each_with_index
  #=> #<Enumerator: [89, 52, 52, 36, 18, 18, 5]:each_with_index>
b.to_a # values to be passed to block
  #=> [[89, 0], [52, 1], [52, 2], [36, 3], [18, 4], [18, 5], [5, 6]]
c = b.chunk { |e,_| e }
  #=> #<Enumerator: #<Enumerator::Generator:0x000001010e39e8>:each>
c.to_a # values to be passed to block
  #=> [[89, [[89, 0]]],
  #    [52, [[52, 1], [52, 2]]],
  #    [36, [[36, 3]]],
  #    [18, [[18, 4], [18, 5]]],
  #    [5, [[5, 6]]]]
d = c.flat_map { |_,a| [a.first.last+1]*a.size }
  #=> [1, 2, 2, 4, 5, 5, 7]
b=a.每个_都有_索引
#=> #
b、 to_a#要传递给块的值
#=> [[89, 0], [52, 1], [52, 2], [36, 3], [18, 4], [18, 5], [5, 6]]
c=b.chunk{e,124; e}
#=> #
c、 to_a#要传递给块的值
#=> [[89, [[89, 0]]],
#    [52, [[52, 1], [52, 2]]],
#    [36, [[36, 3]]],
#    [18, [[18, 4], [18, 5]]],
#    [5, [[5, 6]]]]
d=c.flat_映射{| |,a |[a.first.last+1]*a.size}
#=> [1, 2, 2, 4, 5, 5, 7]

我认为这不会正确地跳过值?既然第二名的成绩是平局,就不应该有第三名了——应该直接排到第四名。@Aardvark修正。虽然没有那么漂亮,但仍然是迄今为止最简单的(对于长数组,也是性能最好的)解决方案。我只是为了好玩。它甚至更干净,但与我的其他解决方案相比,它在基准测试中的性能不足一半(但仍比@sawa的解决方案快50%)。Ruby的方法调用开销再次出现。递归版本对于1000个随机整数来说真的很糟糕。我想收回我的请求,将我的解决方案添加到基准测试中(不是真的:-)。这个问题已经得到了回答:不,这个不会像我在这里要求的那样跳过列组。aard,为什么要使用Rails标记?有些人,像我一样,过滤掉铁轨。我不知道我是如何偶然发现这个问题的,但如果你有不必要的标签,你可能会错过好的答案。@CarySwoveland-Huh,我猜是我的错误。我在Rails上下文中使用它(这与这个问题无关)。我想我的逻辑一直是在太多的标签上犯错。我从来没有真正考虑过那些可能因为标签太多而忽略问题的人。我将在将来记住它,并从这一个中删除Rails标记。谢谢这是超级聪明的,它与我的七个元素的解决方案保持一致,但是给出了1000个元素(<代码>(0…1000)。map {rand 100 }。排序。反转< /代码>)。糟糕。真糟糕。但还是很聪明@乔丹:这是一条有趣的线索,希望这里有更多的问题,这样就会产生这样的讨论。这也说明了如果性能至关重要,那么真正对代码进行基准测试是多么重要,因为“聪明”的代码并不总是性能最好的代码。我想我当时心情很好。只是你的版本比需要的复杂得多,而且性能也不是特别好,老实说,我很嫉妒你的得票率比我高。嘿,很棒的更新!我已经更新了下面的基准。它的表现非常好,我认为它绝对符合“优雅”的标准。既然你编辑了你的帖子,我就可以把我愚蠢的反对票改为赞成票。我希望@aardvark回来接受你的回答!事实上,现在我来看一下,这与@simongarnier的解决方案完全相同,只是他使用
每个带有索引的\u,而您手动增加一个
I
变量。有趣的是,你的版本快了20%。
a = [89, 52, 52, 36, 18, 18, 5]

ranks(a)
  #=> [1, 2, 2, 4, 5, 5, 7]
b = a.each_with_index
  #=> #<Enumerator: [89, 52, 52, 36, 18, 18, 5]:each_with_index>
b.to_a # values to be passed to block
  #=> [[89, 0], [52, 1], [52, 2], [36, 3], [18, 4], [18, 5], [5, 6]]
c = b.chunk { |e,_| e }
  #=> #<Enumerator: #<Enumerator::Generator:0x000001010e39e8>:each>
c.to_a # values to be passed to block
  #=> [[89, [[89, 0]]],
  #    [52, [[52, 1], [52, 2]]],
  #    [36, [[36, 3]]],
  #    [18, [[18, 4], [18, 5]]],
  #    [5, [[5, 6]]]]
d = c.flat_map { |_,a| [a.first.last+1]*a.size }
  #=> [1, 2, 2, 4, 5, 5, 7]