如何使用Ruby合并和排序多个列表?

时间:2010-10-11 16:35:19

标签: ruby

我有2个包含日期和数据的列表。每个列表按照序列号所指示的顺序排列。现在我需要将两个列表合并在一起,并按正确顺序保存所有内容。

例如:

  

列表A
  20101001 A数据1 seq1
  20101001 A数据2 seq2
  20101005 A数据3 seq3

     

清单B
  20101001 B数据1 seq1
  20101003 B数据2 seq2

     

等...

我需要新列表看起来像这样:

  

20101001 A数据1 seq1
  20101001 A数据2 seq2
  20101001 B数据1 seq3
  20101003 B数据2 seq4
  20101005 A数据3 seq5

我想到的两件事是将列表合并在一起并在将它们插入数据库之前应用序列号,或者我可以使用当前序列将它们插入到数据库中并再次将它们拉回来将它们合并在一起,但是这似乎是一个额外的步骤和kludgy。

有关最佳方式的任何想法吗?

4 个答案:

答案 0 :(得分:5)

假设您的列表位于Ruby Arrays中,并且列表中的对象定义了属性(例如obj.sequence_number),则对列表进行合并和排序的一种方法是:

首先将列表合并为联合:

@merged_list = @list_a | @list_b

然后使用适当的排序规则对merged_list进行排序:

@merged_list.sort! {|a, b| a.date <=> b.date # or whatever your sorting rule is... }

编辑:

合并后的数组排序后,您可以重新定义sequence_number:

@merged_list.each_with_index {|obj, index| obj.sequence_number = "seq#{index+1}"}

编辑:

如果列表中的对象本身只是简单的数组,则同样适用:

@merged_list.sort! {|a, b| a[0] <=> b[0] # or whatever your sorting rule is... }
@merged_list.each_with_index {|obj, index| obj[2] = "seq#{index+1}"}

答案 1 :(得分:0)

试试这个:

(listA + listB).sort!{|a, b| a.sequence_no <=> b.sequence_no}

答案 2 :(得分:0)

这是一种用于在或多或少的线性时间内合并任意数量的排序列表的算法:

def merge_sorted(*lists)
  # the lists will be modified, so make (shallow) copies
  lists = lists.map(&:dup)
  result = []
  loop do
    # ignore lists that have been exhausted
    lists = lists.reject(&:empty?)
    # we're done if all lists have been exhausted
    break if lists.empty?
    # find the list with the smallest first element
    top = lists.inject do |candidate, other|
      candidate.first < other.first ? candidate : other
    end
    result << top.shift
  end
  result
end

list1 = [1, 2, 5, 6, 9]
list2 = [2, 3, 4, 11, 13]
list3 = [1, 2, 2, 2, 3]

p merge_sorted(list1, list2, list3)
  # => [1, 1, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6, 9, 11, 13]

对于每次迭代,它会找到具有最小第一个元素的列表,并将此元素从其中移到结果列表中。它会执行此操作,直到所有列表都为空。

我说或多或少线性时间,因为它实际上是O(n×m),其中n是列表数,m是列表中元素的总数,但我认为这可以安全在大多数情况下,简化为O(m),因为与m相比,n会很小。

答案 3 :(得分:0)

这使用with_index这是向迭代器添加索引值的好方法:

result = (list_a + list_b).sort_by { |a| a[0 .. -2] }.map.with_index { |a, i| a[0 .. -2] + (1 + i).to_s }
puts result
# >> 20101001 A data 1 seq1
# >> 20101001 A data 2 seq2
# >> 20101001 B data 1 seq3
# >> 20101003 B data 2 seq4
# >> 20101005 A data 3 seq5

以下是基准测试的一些变体:

require 'benchmark'

list_a = [
  '20101001 A data 1 seq1',
  '20101001 A data 2 seq2',
  '20101005 A data 3 seq3'
]

list_b = [
  '20101001 B data 1 seq1',
  '20101003 B data 2 seq2'
]

# #1
result = (list_a + list_b).sort_by { |a| a[0 .. -2] }.map.with_index { |a, i| a[0 .. -2] + (1 + i).to_s }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]

# #2
result = (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map.with_index { |a, i| a + (1 + i).to_s }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]

# #3
i = 0
result = (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map { |a| i += 1; a + i.to_s }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]

# #4
i = 0; result = (list_a + list_b).sort.map { |a| i += 1; a[-1] = i.to_s; a }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]

n = 75000
Benchmark.bm(7) do |x|
  x.report('#1') { n.times { (list_a + list_b).sort_by { |a| a[0 .. -2] }.map.with_index { |a, i| a[0 .. -2] + (1 + i).to_s } } } 
  x.report('#2') { n.times { (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map.with_index { |a, i| a + (1 + i).to_s } } }
  x.report('#3') { n.times { i = 0; (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map { |a| i += 1; a + i.to_s } } }
  x.report('#4') { n.times { i = 0; (list_a + list_b).sort.map { |a| i += 1; a[-1] = i.to_s } } }
end
# >>              user     system      total        real
# >> #1       1.150000   0.000000   1.150000 (  1.147090)
# >> #2       0.880000   0.000000   0.880000 (  0.880038)
# >> #3       0.720000   0.000000   0.720000 (  0.727135)
# >> #4       0.580000   0.000000   0.580000 (  0.572688)

基准测试很好。