内置方法在ruby中合并两个排序列表

时间:2011-11-10 02:04:56

标签: ruby sorting

我有两个Foo对象列表。每个Foo对象都有一个时间戳Foo.timestamp。两个列表最初按时间戳按降序排序。

我想合并两个Foo对象列表,其中最终列表也按时间戳按降序排序。

实现这一点并不难,但我想知道是否有任何内置的Ruby方法可以做到这一点,因为我认为内置方法将产生最佳性能。

感谢。

4 个答案:

答案 0 :(得分:4)

这样可行,但它不会提供很好的性能,因为它不会利用事先已经排序的列表:

list = (list1 + list2).sort_by(&:timestamp)

我不知道任何内置函数能够达到你想要的效果。

答案 1 :(得分:2)

我快速查看了Array#sortEnumerable#sort实施,两者似乎都使用了quicksort。因此,他们可能可能不如您手动使用merge sort那样高效,而a nice article about self-implemented sorting algorithms依次选择两个潜在元素中的哪一个输出到新列表。

然而,Enumerable#sort_by表明,一个程序员比底层快速排序做得更好的努力失败了 - 他没有直接解决你遇到的问题,但他的数字令人生畏,我尝试了默认{首先排序{3}},只有当感觉太慢时才会返回尝试自编合并排序。

答案 2 :(得分:2)

合并两个臭臭的势在必行...

a = [1,3,7,11]
b = [2,4,6,14]

c = merge_sorted_arrays(a,b)

def merge_sorted_arrays(a,b)
  a.reverse!
  b.reverse!
  output = []

  loop do
    break if a.empty? || b.empty?
    output << (a.last < b.last ? a.pop : b.pop)
  end
  return output + a.reverse + b.reverse
end

也许正在使用.slice!拿第一个元素会比倒转和弹出更好吗?

====================

第四条评论后编辑

是的......我还有另一场戏,但需要继续实际工作,否则我会被解雇; - )

在大型整数数组上,我的原始方法比使用sort_by更快,但在使用100,000个OpenStruct对象填充数组并对属性进行排序后,sort_by的速度提高了100倍。

这是我的基准测试结果:

def pop_merge_sorted_arrays(array1,array2)
  array1.reverse!
  array2.reverse!
  output = []

  loop do
    break if array1.empty? || array2.empty?
    output << (array1.last.my_field < array2.last.my_field ? array1.pop : array2.pop)
  end
  return output + array1.reverse + array2.reverse
end

def shift_merge_sorted_arrays(array1,array2)
  output = []
  loop do
    break if array1.empty? || array2.empty?
    output << (array1.first.my_field < array2.first.my_field ? array1.shift : array2.shift)
  end
  return output + array1 + array2
end

def slice_merge_sorted_arrays(array1,array2)
  output = []
  loop do
    break if array1.empty? || array2.empty?
    output << (array1.first.my_field < array2.first.my_field ? array1.slice!(0) : array2.slice!(0))
  end
  return output + array1 + array2
end

a=(1..100000).map{|x|OpenStruct.new(:my_field => rand)}.sort_by(:my_field)
b=(1..100000).map{|x|OpenStruct.new(:my_field => rand)}.sort_by(:my_field)

# method 1
t=Time.now;w=pop_merge_sorted_arrays(a.clone,b.clone);puts Time.now-t
# average of five runs: 185.96seconds

# method 2
t=Time.now;x=shift_merge_sorted_arrays(a.clone,b.clone);puts Time.now-t
# average of five runs: 0.77seconds

# method 3
t=Time.now;y=slice_merge_sorted_arrays(a.clone,b.clone);puts Time.now-t
# average of five runs: 8.46seconds

# method 4
t=Time.now;z=(a.clone + b.clone).sort_by(&:my_field);puts Time.now-t
# average of five runs: 2.13seconds

所以结果似乎是你可以编写一种方法,在保持秩序运行得非常快的同时将它们洗牌(并且可能会有更多的效率来挤出shift_merge方法,但为了额外的好处,是它真的值得只是将它们拼凑在一起并使用sort_by以方便它?: - )

我希望这不算是一个题外话......

答案 3 :(得分:0)

使用extend()方法在list2的末尾添加list1

list1 = ["a", "b" , "c"]
list2 = [1, 2, 3]

list1.extend(list2)
print(list1)