Слияние и чередование двух массивов в Ruby


106

У меня такой код:

a = ["Cat", "Dog", "Mouse"]
s = ["and", "&"]

Я хочу объединить массив sв массив, aкоторый даст мне:

["Cat", "and", "Dog", "&", "Mouse"]

Просматривая документы Ruby Array и Enumerable, я не вижу такого метода, который позволил бы это сделать.

Есть ли способ сделать это без перебора каждого массива?


a всегда будет иметь 3 элемента, а s два? было бы полезно еще несколько примеров.
tokland 05

Ответы:


171

Вы можете сделать это с помощью:

a.zip(s).flatten.compact

4
Что делать, если aэлементов больше 3?
Майкл Коль

116
["a", "b"] .concat (["c", "d"]) # => ["a", "b", "c", "d"]
Лев Романовский

31
@Leo, @chuck: если вы прочитаете пример, вы увидите, что Крис хочет чередовать элементы, а не объединять их. По сути, он хочет [a, s].transpose, чтобы две строки не соответствовали друг другу, оставив #zipочевидное решение. И я не верю, что он имел в виду, что его действительно заботило, aбыл ли мутирован ... Я не думаю, что он вообще комментировал мутированное и функциональное решение, он просто пытался описать шаблон.
DigitalRoss,

15
+1 за то, что был единственным человеком, который на самом деле прочитал этот тупой вопрос! > _ <
Мэтт Флетчер

5
Что еще более важно, что, если два массива имеют неравную длину? Особенно если s длиннее? Думаю, я могу с уверенностью предположить, что приведенный Крисом пример - это не реальные данные, с которыми он работает. подумайте: [] .zip [1, 2] => nil (будет трудно вызвать #flatten в этом случае) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (ой, думаю, нас не волнуют последние несколько элементов во втором массиве)
hoff2

32

Это не даст результирующий массив в том порядке, который запрашивал Крис, но если порядок результирующего массива не имеет значения, вы можете просто использовать a |= b. Если вы не хотите мутировать a, вы можете записать a | bи присвоить результат переменной.

См. Документацию по объединению набора для класса Array по адресу http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Этот ответ предполагает, что вам не нужны повторяющиеся элементы массива. Если вы хотите разрешить дублирование элементов в окончательном массиве, это a += bдолжно помочь. Опять же, если вы не хотите изменять a, используйте a + bи присвойте результат переменной.

В ответ на некоторые комментарии на этой странице, эти два решения будут работать с массивами любого размера.


Этот определенно кажется лучшим.
ardavis

12
Это дает ["Cat", "Dog", "Mouse", "and", "&"], что не то, что хотел OP.
Эндрю Гримм

Отличный звонок, Андрей. Я обновлю свой ответ, чтобы сказать, что я не отвечал на вопрос Криса.
Майкл Сталкер

29

Если вы не хотите дублировать, почему бы просто не использовать оператор объединения :

new_array = a | s

1
Присуждение +1 за недооцененное, простое и элегантное решение.
Giacomo1968

Конечно, это ответ на вопрос! Вопрос был: «Я хочу объединить массив s в массив a»
Дуглас

Хорошее решение, но это меняет порядок результатов. Результаты sбудут в конце нового массива.
Хендрик

2
Однако порядок элементов не будет таким, каким хотел OP.
tokland

6
s.inject(a, :<<)

s   #=> ["and", "&"]
a   #=> ["Cat", "Dog", "Mouse", "and", "&"]

Он не дает вам того порядка, который вы просили, но это хороший способ объединения двух массивов путем добавления к одному.


Мне нравится, коротко и чисто. :)
Нафаа Бутефер

6

Вот решение, которое позволяет чередовать несколько массивов разного размера (общее решение):

arr = [["Cat", "Dog", "Mouse", "boo", "zoo"],
 ["and", "&"],
 ["hello", "there", "you"]]

first, *rest = *arr; first.zip(*rest).flatten.compact
=> ["Cat", "and", "hello", "Dog", "&", "there", "Mouse", "you", "boo", "zoo"]

2
Ницца! Одно ограничение: первый массив должен быть самым длинным.
Брайан Лоу

@BrianLow отличный улов!
Абдо

5

Это не совсем элегантно, но работает для массивов любого размера:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]

+1 для работы со странными крайними случаями, я думаю, i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]было бы в равной степени справедливо.
mu слишком короткое

Я не был уверен , что , если ОП хочет чередовать andи &, таким образом , я взял его в буквальном смысле , как это возможно, допуская при этом aлюбой длины.
Michael Kohl

3

Как насчет более общего решения, которое работает, даже если первый массив не самый длинный и принимает любое количество массивов?

a = [
    ["and", "&"],
    ["Cat", "Dog", "Mouse"]
]

b = a.max_by(&:length)
a -= [b]
b.zip(*a).flatten.compact

 => ["Cat", "and", "Dog", "&", "Mouse"]

2

Чтобы справиться с ситуацией, когда оба a& sимеют разные размеры:

a.zip(s).flatten.compact | s
  • .compactудалит, nilкогда aбольше чемs
  • | sдобавит оставшиеся элементы, sкогда aменьше чемs

1

Один из способов сделать чередование, а также гарантировать, какой из них является самым большим массивом для метода zip, - это заполнить один из массивов nilдо тех пор, пока не будет размер другого массива. Таким образом, вы также гарантируете, какой элемент какого массива будет на первой позиции:

preferred_arr = ["Cat", "Dog", "Mouse"]
other_arr = ["and","&","are","great","friends"]

preferred_arr << nil while preferred_arr.length < other_arr.length
preferred_arr.zip(other_arr).flatten.compact
#=> ["Cat", "and", "Dog", "&", "Mouse", "are", "great", "friends"]

1
Немного лучше: preferred_arr.zip(other_arr).flatten | other_arr(без обратной засыпки нуля)
Адам Фендли

-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]

5
извините ... не заметил конкретный порядок, в котором вы хотели выводить данные. Извиняюсь за попытку помочь, больше не повторится.
Дэвид Морроу,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.