假设它们在 Ruby 中共享一个元素,如何合并不同长度的嵌套数组?

How to merge nested arrays of varying lengths, given they share an element in Ruby?

我有由字符串和整数组成的嵌套数组,我想根据每个数组的第一个元素合并它们。假设这些是项目 ID。嵌套数组的长度不同,但只要每个嵌套数组的第一个元素相同,我想将它们合并在一起。

我的数据可能是这样的:

arr = [['00', 'apples', nil, nil, 8],
['00', 'apples', nil, 2],
['00', 'apples', nil, nil, nil, 3],
['01', 'bananas', 2],
['01', 'bananas', nil, nil, 3],
['01', 'bananas', nil, 5]]

所以我想压缩这些数组,使它们看起来像:

arr = [['00', 'apples', nil, 2, 8, 3],
['01', 'bananas', 2, 5, 3]]

对于给定的第一个元素(00 或 01),不会出现第二个元素(在本例中为苹果或香蕉)不同的情况。因此,只要第一个元素在任何两个嵌套数组之间是相同的,我就想将它们合并在一起。

此外,数据保证共享相同第一个元素的数组的数字之间不会发生冲突。但是,数字应保持其索引位置 post-merge。这很关键。

我不太确定如何处理这个问题。我正在考虑创建一个空数组,然后遍历嵌套数组以检查第一个元素是否包含在新数组中。如果不是,将该数组推送到新数组。如果是这样,请合并它们。我的代码不起作用(我得到一个空数组)...

    new_array = Array.new
    old_array.each do |o|
      new_array.each do |n|
        if o[0] == n[0]
          n = o | n
        else
          n.push(o)
        end
      end
    end

我调查了 .reduce(:|) 但它给了我不想要的结果:

arr = [["00", "apples", nil, nil, nil, 8],
 ["00", "apples", nil, nil, nil, nil, 1],
 ["00", "apples", nil, nil, 1]]
arr.reduce(:|)
=> ["00", "apples", nil, 8, 1]

我期待:=> ["00", "apples", nil, nil, 1, 8, 1]

您可以使用散列并将其转换为数组

new_hash = {}
old_array.each do |o|
  new_hash[o[0]] ||= []
  o.each_with_index do |n, i|
    new_hash[o[0]][i] ||= n
  end
end

puts new_hash.inspect
puts new_hash.map { |k,v| v }.inspect

这将导致

{"00"=>["00", "apples", nil, 2, 8, 3], "01"=>["01", "bananas", 2, 5, 3]}
[["00", "apples", nil, 2, 8, 3], ["01", "bananas", 2, 5, 3]]

有几种方法可以做到这一点。一种是使用Enumerable#group_by。其他人可能会提到合并哈希的方法。

arr = [
  ['00', 'apples', nil, nil, 8],
  ['00', 'apples', nil, 2],
  ['00', 'apples', nil, nil, nil, 3],
  ['01', 'bananas', 2],
  ['01', 'bananas', nil, nil, 3],
  ['01', 'bananas', nil, 5]
]

arr.group_by(&:first).values.map { |a| a.reduce(:|) }
  #=> [["00", "apples", nil, 8, 2, 3], ["01", "bananas", 2, nil, 3, 5]]

步骤如下:

b = arr.group_by(&:first)
  #=> {"00"=>[["00", "apples", nil, nil, 8], ["00", "apples", nil, 2],
  #           ["00", "apples", nil, nil, nil, 3]],
  #    "01"=>[["01", "bananas", 2], ["01", "bananas", nil, nil, 3],
  #           ["01", "bananas", nil, 5]]}
c = b.values
  #=> [[["00", "apples", nil, nil, 8], ["00", "apples", nil, 2],
  #     ["00", "apples", nil, nil, nil, 3]],
  #    [["01", "bananas", 2], ["01", "bananas", nil, nil, 3],
  #     ["01", "bananas", nil, 5]]]
c.map { |a| a.reduce(:|) }     
  #=> [["00", "apples", nil, 8, 2, 3], ["01", "bananas", 2, nil, 3, 5]]

c的第一个元素传递给map的块并赋值给块变量:

a = [["00", "apples", nil, nil, 8], ["00", "apples", nil, 2],
     ["00", "apples", nil, nil, nil, 3]]

a.reduce(:|) 给出与 a.reduce { |d,e| d | e } 相同的结果,它只是 a:

的三个元素(数组)的并集
(["00", "apples", nil, nil, 8] | ["00", "apples", nil, 2]) |
["00", "apples", nil, nil, nil, 3]]
  #=> ["00", "apples", nil, 8, 2] | ["00", "apples", nil, nil, nil, 3] 
  #=> ["00", "apples", nil, 8, 2, 3]

对传递给 map 块的 a 的第二个元素执行类似的计算。