Ruby:多维数组到一维行

时间:2017-09-16 13:58:54

标签: arrays ruby multidimensional-array

在Ruby中,我该如何解决这个问题:

[
  1, 
  ["green", "blue", "black"], 
  [ ["1", "2"], ["3"], ["4", "5"] ]
]

到此?

[
  [1, "green", "1"],
  [1, "green", "2"],
  [1, "blue", "3"],
  [1, "black", "4"],
  [1, "black", "5"],
]

我试过.zip但没有运气。非常感谢任何帮助,当然我正在寻找一个高效的解决方案。

3 个答案:

答案 0 :(得分:3)

模式对我来说并不完全清楚,但这会得到您提供的示例的预期输出:

data[1].
  zip(data[2]).
  flat_map { |x, ys| [x].product(ys) }.
  map { |zs| [data[0], *zs] }
#=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"], 
#    [1, "black", "4"], [1, "black", "5"]]

答案 1 :(得分:2)

我们得到了

arr = [1, ["green", "blue", "black"], [ ["1", "2"], ["3"], ["4", "5"] ]]

以下是获得所需结果的两种方法。

<强>#1

arr[1].flat_map.with_index { |color,i| [arr[0]].product([color], arr[2][i]) }
  #=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"],
  #    [1, "black", "4"], [1, "black", "5"]]

步骤如下。

enum0 = arr[1].flat_map
  #=> #<Enumerator: ["green", "blue", "black"]:flat_map>
enum1 = enum0.with_index
  #=> #<Enumerator: #<Enumerator: ["green", "blue", "black"]:flat_map>:with_index>

enum1可以被视为复合枚举器。我们可以看到enum1生成的值,并通过将其转换为数组传递给块。

enum1.to_a
  #=> [["green", 0], ["blue", 1], ["black", 2]]

生成第一个值并传递给块,分配块变量并执行块计算。

color, i = enum1.next
  #=> ["green", 0]
color
  #=> "green"
i #=> 0
[arr[0]].product([color], arr[2][i])
  #=> [1].product(["green"], )
  #=> [[1, "green", "1"], [1, "green", "2"]]

enum1生成的其余两个元素的计算类似。

替代方法是复制arr[2]并移动dup的元素:

a2 = arr[2].dup
arr[1].flat_map { |color,i| [arr[0]].product([color], a2.shift) }

<强>#2

arr[1].zip(arr[2]).flat_map { |color, a| [arr[0]].product([color], a) }
  #=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"],
  #    [1, "black", "4"], [1, "black", "5"]]

步骤如下。

b = arr[1].zip(arr[2])
  #=> [["green", ["1", "2"]], ["blue", ["3"]], ["black", ["4", "5"]]]

b[0]传递给flat_map并分配块变量并执行块计算。

color, a = b[0]
  #=> ["green", ["1", "2"]]
color
  #=> "green"
a #=> ["1", "2"]
[arr[0]].product([color], a)
  #=> [["1"]].product(["green"], ["1", "2"])
  #=>  [[1, "green", "1"], [1, "green", "2"]]

b的其余元素传递给map后,Enumerable#flat_map会返回所需的数组。

答案 2 :(得分:1)

与提议的解决方案相比,我需要更通用和灵活的解决方案(我的不好,我应该更清楚地了解要求),所以我想出了以下内容:

class Array
  def transpose_rows
    arys = self.select{|el| el.is_a?(Array)}
    if arys.size == 0
      [self]
    else 
      result = []
      (arys.map(&:size).max || 1).times.map{ |i|
        self.map { |r| 
          r.is_a?(Array) ? r[i] : r
        }.transpose_rows.map{|r| result << r}
      }
      result
    end
  end
end

初始规范是数组中的每个元素都是一个值或另一个深度不同的数组。每个子阵列将 depth-1 的子阵列的值“爆炸”为任意数量的“子值”。结果应该是一组行,列出从原始数组派生的所有组合。

提出的其他解决方案适用于我发布的原始数组,这只是一个示例,而这个解决方案适用于更复杂的方案,如下所示:

[
  2, 
  [3,4,5], 
  6, 
  [7,8,9], 
  [ [11,22], [33], [44,55] ], 
  [0, 1, 2],
  [ [66], [77], [nil,99] ],
  4
].transpose_rows

# => [
#   [2, 3, 6, 7, 11, 0, 66, 4], 
#   [2, 3, 6, 7, 22, 0, nil, 4], 
#   [2, 4, 6, 8, 33, 1, 77, 4], 
#   [2, 5, 6, 9, 44, 2, nil, 4], 
#   [2, 5, 6, 9, 55, 2, 99, 4]
# ]