如何在Ruby中按特定顺序对数组进行排序?

32
我想按另一个数组中给定的特定顺序对数组进行排序。
例如:考虑一个数组
a=["one", "two", "three"]
b=["two", "one", "three"]

现在我想按照'b'的顺序对数组'a'进行排序,即
a.each do |t|
  # It should be in the order of 'b'
  puts t
end

所以输出应该是:
two
one 
three 

任何建议?

2
就目前而言,这个问题没有意义。示例显示了两个具有完全相同值的数组(仅顺序不同)。如果您想按b中找到的顺序迭代a中的元素,那么迭代b即可完成 :-) 所以我猜还有更多条件,也许数组中的项目不匹配?你需要的不是对象之间的“==”,而是一种不同的相等性吗?请展示一些更有意义的例子。 - tokland
3个回答

57

Array#sort_by 是你要寻找的方法。

a.sort_by do |element|
  b.index(element)
end

响应评论的更可扩展版本:

a=["one", "two", "three"]
b=["two", "one", "three"]

lookup = {}
b.each_with_index do |item, index|
  lookup[item] = index
end

a.sort_by do |item|
  lookup.fetch(item)
end

1
对于小数组来说,这是最简单的方法,但请注意,当问题是O(n)时,这将是O(n^2)。 - tokland
@tokland 好的。提供了一个更可扩展的版本。 - Andrew Grimm
准确地说,创建一个辅助映射+sort_by。我可能会这样写:lookup = Hash[b.to_enum.with_index],但这只是一个细节。顺便问一下,你看到我对问题的评论了吗?你还记得OP的想法是什么吗? - tokland
@tokland和我可能会使用a.sort_by(&lookup.method(:fetch))。我怀疑即使是OP也不记得自己当时的想法了。但有时我会根据属性对对象数组进行排序,其中另一个数组包含与该属性相等的值。 - Andrew Grimm
1
是的,在给定属性上进行相等比较非常常见。我知道你喜欢使用 &:obj.method(:name) 这个技巧,我还记得你在博客中写过它 :-) - tokland
显示剩余2条评论

12

如果b包含所有a的元素且这些元素是唯一的,则:

puts b & a

如果满足该条件,则b&a == b - Pan Thomakos
1
@PanThomakos,它可以包含元素,也可以有其他元素。%w {ameba bug bird dog horse shark}&%w {horse ameba shark} => [“ameba”,“horse”,“shark”] - Nakilon
1
我认为这很酷,但是我有一些保留意见:
  1. 使用&(集合交集)对数组进行排序是具有误导性的。
  2. 代码脆弱。如果列表具有重复元素或大小不正确,则代码会出错。如果&的实现发生更改,使元素不再排序,则代码会出错。
- Pan Thomakos

10
假设要按照b中元素的顺序对a进行排序。
sorted_a = 
a.sort do |e1, e2|
  b.index(e1) <=> b.index(e2)
end

我通常使用这个方法来按照表单上字段的出现顺序对ActiveRecord中的错误信息进行排序。


1
为什么要使用 sort,而不是使用 sort_by - Andrew Grimm
性能。http://ruby-doc.org/core/classes/Enumerable.html#M003120 查看基准测试。 - Chirantan
2
我的基准测试显示 sort 需要 N 秒,sort_by 需要 0.07 N 秒,而 & 则需要 0.01 N 秒。 - Nakilon
2
因为在sort_by中,索引仅计算一次用于每个元素,而在sort中只有在进行缓存时才可能。而且&是Ruby中编译的。 - Nakilon
4
该文档中的基准测试将没有块的 sort 与使用(无操作)块的 sort_by 进行了比较。如果您将带有块的 sort(例如此答案中的示例)与相应的 sort_by 版本进行比较,您会发现 sort_by 要快得多。 - glenn mcdonald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接