Об’єднайте і переплетете два масиви в Ruby


106

У мене є такий код:

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

Я хочу об'єднати масив sу масив, aякий би дав мені:

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

Переглядаючи масив Ruby та безліч документів, я не бачу такого способу, який би це досяг.

Чи є спосіб я це зробити, не повторюючи кожен масив?


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

Відповіді:


171

Це можна зробити за допомогою:

a.zip(s).flatten.compact

4
Що робити, якщо aмає більше 3 елементів?
Майкл Коль

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

30
@Leo, @chuck: якщо ти прочитаєш приклад, то побачиш, що Кріс хоче переплутати елементи, а не з'єднувати їх. По суті, він хоче, [a, s].transposeкрім того, щоб два ряди не відповідали, залишаючи #zipочевидним рішенням. І я не вірю, що він мав на увазі, що він по-справжньому дбав про те, чи aбув мутований ... Я не думаю, що він взагалі коментував мутаційне проти функціонального рішення, він просто намагався описати схему.
DigitalRoss

15
+1 за те, що є єдиною людиною, яка насправді прочитала запитання блима! > _ <
Метт Флетчер

5
Що ще важливіше, що робити, якщо два масиви мають неоднакову довжину? Особливо, якщо s - довший? Я думаю, що можу з упевненістю припустити, що приклад Кріса - це не фактичні дані, з якими він працює врахуйте: [] .zip [1, 2] => нуль (буде важко викликати #flatten з цього приводу) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (ой, думаю, нас не хвилює останні кілька елементів у 2-му масиві)
hoff2

32

Це не дасть масив результатів у тому порядку, про який просив Кріс, але якщо порядок отриманого масиву не має значення, ви можете просто скористатися a |= b. Якщо ви не хочете мутувати a, ви можете написати a | bта призначити результат змінній.

Дивіться встановлену документацію об'єднання для класу Array на веб- сайті http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Ця відповідь передбачає, що ви не хочете дублювати елементи масиву. Якщо ви хочете дозволити повторювані елементи у вашому остаточному масиві, a += bслід зробити фокус. Знову ж таки, якщо ви не хочете мутувати a, використовуйте a + bта призначте результат змінній.

У відповідь на деякі коментарі на цій сторінці ці два рішення працюватимуть з масивами будь-якого розміру.


Цей, безумовно, здається найкращим.
Ардавіс

11
Це дає ["Cat", "Dog", "Mouse", "and", "&"], що не те, чого хотів ОП.
Ендрю Грімм

Відмінний дзвінок, Ендрю. Я оновлю свою відповідь, щоб сказати, що не відповідав на питання Кріса.
Майкл Сталкер

29

Якщо ви не хочете дублювати, чому б просто не скористатися оператором об'єднання :

new_array = a | s

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

Звичайно, це відповідає на питання! Питання було: "Я хочу об'єднати масив s у масив a"
Дуглас

Гарне рішення - але це змінює порядок результатів. Результати з sбуде в кінці нового масиву.
Гендрік

1
Але порядок елементів не буде таким, як хотіла ОП.
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будь-якої тривалості.
Майкл Коль

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(без нульової засипки)
Адам Фендлі

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.