3

我想将一个 Ruby 数组(可能包含一些子数组)扩展为另一个数组数组,就像在这些示例中一样:

示例 1:[:foo, :bar]

[
  [:foo, :bar]
]

示例 2:[:foo, :bar, [:ki, :ku]]

[
  [:foo, :bar, :ki],
  [:foo, :bar, :ku]
]

示例 3:[:foo, :bar, :baz, [:a, :i, :u, :e, :o], :qux]

[
  [:foo, :bar, :baz, :a, :qux],
  [:foo, :bar, :baz, :i, :qux],
  [:foo, :bar, :baz, :u, :qux],
  [:foo, :bar, :baz, :e, :qux],
  [:foo, :bar, :baz, :o, :qux]
]

示例 4:[:foo, :bar, :baz, [:a, :i, :u, :e, :o], [1, 2], :qux]

[
  [:foo, :bar, :baz, :a, 1, :qux],
  [:foo, :bar, :baz, :i, 1, :qux],
  [:foo, :bar, :baz, :u, 1, :qux],
  [:foo, :bar, :baz, :e, 1, :qux],
  [:foo, :bar, :baz, :o, 1, :qux],
  [:foo, :bar, :baz, :a, 2, :qux],
  [:foo, :bar, :baz, :i, 2, :qux],
  [:foo, :bar, :baz, :u, 2, :qux],
  [:foo, :bar, :baz, :e, 2, :qux],
  [:foo, :bar, :baz, :o, 2, :qux]
]

示例 5:[:foo, [[], :c], :bar]

[
  [:foo, [], :bar],
  [:foo, :c, :bar]
]

示例 6:[:foo, [[:a, :b], :c], :bar]

[
  [:foo, [:a, :b], :bar],
  [:foo, :c, :bar]
]

注意:仅应扩展子数组。这就是为什么在示例 5 和 6 中,子子数组没有展开。

非常感谢您的任何建议或代码。

4

3 回答 3

8

我用这个product想法来实现这个功能:

def trans(a)
  b = a.map{|e| [e].flatten(1)}
  b.first.product(*b.slice(1..-1))
end

例如,这段代码:

puts trans([:foo, :bar]).inspect
puts trans([:foo, :bar, :baz, [:a, :i, :u, :e, :o], [1, 2], :qux]).inspect
puts trans([:foo, [[], :c], :bar]).inspect
puts trans([:foo, [[:a, :b], :c], :bar]).inspect

给出了这个:

[[:foo, :bar]]
[[:foo, :bar, :baz, :a, 1, :qux],
 [:foo, :bar, :baz, :a, 2, :qux],
 [:foo, :bar, :baz, :i, 1, :qux],
 [:foo, :bar, :baz, :i, 2, :qux],
 [:foo, :bar, :baz, :u, 1, :qux],
 [:foo, :bar, :baz, :u, 2, :qux],
 [:foo, :bar, :baz, :e, 1, :qux],
 [:foo, :bar, :baz, :e, 2, :qux],
 [:foo, :bar, :baz, :o, 1, :qux],
 [:foo, :bar, :baz, :o, 2, :qux]]
[[:foo, [], :bar],
 [:foo, :c, :bar]]
[[:foo, [:a, :b], :bar],
 [:foo, :c, :bar]]

编辑:上面代码的解释。

一般的想法是我们想要数组中所有元素的乘积。如果您查看Array#product的文档,您会发现它可以满足您的要求——我们只需要适当地调用它。

首先,product对数组进行操作,因此我们必须确保原始数组中的所有项目本身都是数组。这是函数第一行的任务:

b = a.map{|e| [e].flatten(1)}

我们正在使用 转换数组中的所有元素map。转换创建了一个包含e内部元素的数组,然后展平这个新数组。原始元素要么是数组,要么不是;如果它不是一个数组,[e].flatten(1)则什么都不做并返回[e];如果它是一个数组,[e]将评估为[[x]],然后将其展平为[x]1告诉只有flatten1 级深。

然后我们所要做的就是调用product第一个元素作为参数传递修改后的数组的剩余元素:

b.first.product(*b.slice(1..-1))

这里,b.slice(1..-1)意思是:从 b 中取元素,从第 2 个开始一直到最后一个。最后,星号表示我们不想将数组作为参数传递,而是传递数组的元素。

于 2012-03-11T16:15:17.967 回答
2

您似乎希望得到相关数组元素的笛卡尔积。此代码应该适合您:

array = [:foo, :bar, :baz, [:a, :i, :u, :e, :o], [1, 2], :qux]
array.inject([[]]) do |product,element|
  result = []
  if element.is_a?(Array)
    product.each do |tuple|
      element.each do |last|
        result << tuple + [last]
      end
    end
  else
    product.each do |tuple|
      result << tuple + [element]
    end
  end
  result
end

您可以通过将条件移动到循环来稍微简化它,但这会降低效率。

于 2012-03-11T16:07:39.767 回答
1

考虑到您使用的是示例而不是明确的内容,我建议您通过Array 方法的文档进行拖网。对于初学者,请查看以下方法:

.combination.to_a
.shift
.transpose
.flatten
.zip
.take

你将如何实现取决于你是否知道在每种情况下你正在转换什么,或者你是否试图构建一些通用的东西(即扩展 Array 类。对于每个我将输入数组操作到目标数组中。示例 1 是简单的:

input = [:foo,:bar]
target = Array.new
target << input        => [[:foo,:bar]]

对于其余的示例,有多种方法可以到达那里,具体取决于您要做什么,以及您希望 ruby​​ 在收到输入时如何知道该做什么。在示例 2 中,第一个是直接写:

input = [:foo, :bar, [:ki, :ku]]
target = Array.new

target << [input[0], input[1], input[2][0]]
target << [input[0], input[1], input[2][1]]

或者玩数组方法:

target = input.pop
target = [input, input].zip(target).flatten
target = [target[0..(target.size/2)-1], target[target.size/2..-1]]

或者,如果您不知道数组的哪个部分包含子数组,您可以检测到它:

input.each do |i|
  if i.class == Array
    holding = i
  end
end

这实际上取决于您要如何识别和操作数组!

于 2012-03-11T15:51:50.497 回答