Как зациклить два массива и создать карту в Ruby

Я новичок в Ruby и пробую несколько примеров по этой ссылке .

Скажем, у меня есть два массива

Array1: ["square", "circle", "triagle"]

Array2: ["red", "blue"]

Я хочу создать карту, такую ​​как,

[{:shape=>"square", :color=>"red"}]
[{:shape=>"square", :color=>"blue"}]
[{:shape=>"circle", :color=>"red"}]
[{:shape=>"circle", :color=>"blue"}]
[{:shape=>"triangle", :color=>"red"}]
[{:shape=>"triangle", :color=>"blue"}]

Вот код, который я пробовал.

def processArray6(array1, array2)
    newMap = [array1].map do |entry|
    {
        shape: entry,
        color: "abc" # Should write another for-loop here to loop over array2
    }
    end
end

array1 = ["square", "circle", "triangle"]
array2 = ["red", "blue,"]

p processArray6(array1, array2)

Выход для вышеуказанного кода:

[{:shape=>["square", "circle", "triangle"], :color=>"abc"}]

Я не очень уверен, как перебрать массив2.

Я из Java-фона и все еще пытаюсь понять, как целая карта возвращается из функции и как обрабатывать каждый элемент массива и создавать карту.

Всего 4 ответа


Если вам нужен массив хешей, где каждый хеш имеет shape и color ключей, то вы можете использовать product между array1 и array2, а затем просто отобразить результат этого:

array1.product(array2).map { |shape, color| { shape: shape, color: color } }
# [{:shape=>"square", :color=>"red"}, {:shape=>"square", :color=>"blue"}, {:shape=>"circle", :color=>"red"}, {:shape=>"circle", :color=>"blue"}, {:shape=>"triagle", :color=>"red"}, {:shape=>"triagle", :color=>"blue"}]

Следующее вернет массив хэшей

def processArray6(array1, array2)

array3 = []

array2.each{|color| array1.each{|shape| array3 << {:shape => shape, :color => color}}}

return array3

end


>> processArray6(array1, array2)
=> [{:color=>"red", :shape=>"square"}, {:color=>"red", :shape=>"circle"}, {:color=>"red", :shape=>"triagle"}, {:color=>"blue", :shape=>"square"}, {:color=>"blue", :shape=>"circle"}, {:color=>"blue", :shape=>"triagle"}]

Вы хотите вычислить «продукт» из двух списков, что на самом деле очень просто, поскольку в Ruby есть функция product специально для этого:

KEYS = %i[ shape color ]

def all_shape_colors(shapes, colors)
  shapes.product(colors).map do |pair|
    KEYS.zip(pair).to_h
  end
end

Здесь используется zip для ввода двух записей в каждой паре.

На практике это выглядит так:

shapes = %w[ square circle triagle ]

colors = %w[ red blue ]

all_shape_colors(shapes, colors)
# => [{:shape=>"square", :color=>"red"}, {:shape=>"square", :color=>"blue"}, {:shape=>"circle", :color=>"red"}, {:shape=>"circle", :color=>"blue"}, {:shape=>"triagle", :color=>"red"}, {:shape=>"triagle", :color=>"blue"}]

Обратите внимание на использование %w[ xy ] вместо [ "x", "y" ] как способа выражения того же массива с более минимальным синтаксисом. %i[ ... ] то же самое, но возвращает массив символов вместо строк.

На самом деле вы можете пойти еще дальше и создать очень общий метод, который сделает эту комбинацию для вас на произвольном количестве вещей:

def hash_product(**things)
  keys = things.keys

  things.values.inject do |s, options|
    # Compute product and flatten, necessary for chains > length 2
    s.product(options).map(&:flatten)
  end.map do |set|
    keys.zip(set).to_h
  end
end

Где вы сейчас используете это так:

hash_product(shape: shapes, color: colors)

Это означает, что нет необходимости в фиксированном наборе ключей. Все будет работать:

hash_product(hair: %w[ red blue green ], hat: %w[ white black yellow ], shoes: %w[ orange brown tan])
# => [{:hair=>"red", :hat=>"white", :shoes=>"orange"}, {:hair=>"red", :hat=>"white", :shoes=>"brown"}, ... ]

Использование Array # product обычно используется здесь, но он возвращает массив (из которого построен хеш), который может потреблять чрезмерный объем памяти, если два массива, произведение которых вычисляется, велики. Вот способ получения хэшей без использования product (или создания временного массива другими способами).

keys = [:shape, :color]
arr1 = ["square", "circle", "triangle"]
arr2 = ["red", "blue"]

arr1.each_with_object([]) do |shape, arr|
  arr2.each { |color| arr << keys.zip([shape, color]).to_h }
end
  #=> [{:shape=>"square", :color=>"red"},
  #    {:shape=>"square", :color=>"blue"},
  #    {:shape=>"circle", :color=>"red"},
  #    ...
  #    {:shape=>"triangle", :color=>"blue"}]              

Любопытно, что нет метода Array который производит перечислитель, который генерирует элементы в массиве, возвращаемом Array#product . Это достаточно легко сделать. (См. Enumerator :: new .)

class Array
  def product_each(arr)
    Enumerator.new do |y|
      each { |e| arr.each { |f| y << [e,f] } }
    end
  end
end

потом

enum = arr1.product_each(arr2)
loop { p enum.next }

дисплеи:

["square", "red"]
["square", "blue"]
["circle", "red"]
["circle", "blue"]
["triangle", "red"]
["triangle", "blue"]

что позволяет нам вычислить:

arr1.product_each(arr2).map { |pair| keys.zip(pair).to_h }
  #=> [{:shape=>"square", :color=>"red"},
  #    {:shape=>"square", :color=>"blue"},
  #    {:shape=>"circle", :color=>"red"},
  #    ...
  #    {:shape=>"triangle", :color=>"blue"}]              

без создания временного массива.


Есть идеи?

10000