Переведите массив в несколько массивов - PullRequest
3 голосов
/ 11 марта 2012

Я хотел бы расширить массив 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 вложенные массивы не раскрываются.

Большое спасибо за любые предложения или кусок кода.

Ответы [ 3 ]

8 голосов
/ 11 марта 2012

Я использовал идею 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 указывает flatten идти только на 1 уровень глубины.

Тогда все, что нам нужно сделать, это вызвать product для первого элемента, передав в качестве аргументов остальные элементы измененного массива:

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

Здесь b.slice(1..-1) означает: взять элементы из b, начиная со 2-го вплоть до последнего.Наконец, звездочка указывает, что мы не хотим передавать массив в качестве аргумента, а вместо этого элементы массива.

2 голосов
/ 11 марта 2012

Кажется, вы хотите иметь декартово произведение элементов рассматриваемого массива. Этот код должен работать для вас:

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

Вы можете немного упростить его, переместив условное выражение в цикл, но это сделает его менее эффективным.

1 голос
/ 11 марта 2012

Учитывая, что вы используете примеры, а не что-то явное, я бы предложил провести трал в документации метода Array .Для начала взгляните на следующие методы:

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

Способ реализации зависит от того, знаете ли вы, что вы преобразуете в каждом случае, или пытаетесь создать что-то общее (например, расширить массивкласс. Для каждого я бы манипулировал входным массивом в целевой массив. Пример 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

Это действительно зависит от того, как вы хотите идентифицировать и манипулироватьмассив!

...