Połącz i przeplot dwie tablice w Rubim

106

Mam następujący kod:

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

Chcę scalić tablicę sw tablicę, aco dałoby mi:

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

Przeglądając Ruby Array i Enumerable docs, nie widzę takiej metody, która to umożliwi.

Czy istnieje sposób, w jaki mogę to zrobić bez iteracji po każdej tablicy?

Chris Ledet
źródło
a zawsze będzie miał 3 elementy, a s dwa? przydałoby się więcej przykładów.
tokland,

Odpowiedzi:

171

Możesz to zrobić za pomocą:

a.zip(s).flatten.compact
DigitalRoss
źródło
4
A jeśli ama więcej niż 3 elementy?
Michael Kohl,
116
["a", "b"] .concat (["c", "d"]) # => ["a", "b", "c", "d"]
Leo Romanovsky
31
@Leo, @chuck: jeśli przeczytasz przykład, zobaczysz, że Chris chce przeplatać elementy, a nie łączyć je. Zasadniczo chce, [a, s].transposeale te dwa rzędy nie są zgodne, pozostawiając #zipjako oczywiste rozwiązanie. I nie sądzę, żeby miał na myśli, że naprawdę obchodziło go, czy azostał zmutowany… Nie sądzę, żeby w ogóle komentował rozwiązanie zmutowane i funkcjonalne, po prostu próbował opisać wzór.
DigitalRoss
15
+1 za bycie jedyną osobą, która faktycznie przeczytała pytanie blummina! > _ <
Matt Fletcher,
5
Co ważniejsze, co się stanie, jeśli dwie tablice mają nierówne długości? Zwłaszcza jeśli s jest dłuższe? Myślę, że mogę bezpiecznie założyć, że przykład podany przez Chrisa nie jest faktycznymi danymi, z którymi pracuje. rozważ: [] .zip [1, 2] => nil (będzie ciężko wywołać #flatten w tej sprawie) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (ups, chyba nie obchodzi nas kilka ostatnich elementów w drugiej tablicy)
hoff2
32

To nie da tablicy wyników w kolejności, o którą prosił Chris, ale jeśli kolejność wynikowej tablicy nie ma znaczenia, możesz po prostu użyć a |= b. Jeśli nie chcesz mutować a, możesz napisać a | bi przypisać wynik do zmiennej.

Zobacz dokumentację set union dla klasy Array pod adresem adresem http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Ta odpowiedź zakłada, że ​​nie chcesz zduplikowanych elementów tablicy. Jeśli chcesz zezwolić na zduplikowane elementy w ostatecznej tablicy, a += bpowinieneś załatwić sprawę. Ponownie, jeśli nie chcesz mutowaća , użyj a + bi przypisz wynik do zmiennej.

W odpowiedzi na niektóre komentarze na tej stronie, te dwa rozwiązania będą działać z tablicami o dowolnym rozmiarze.

Michael Stalker
źródło
Ten zdecydowanie wydaje się najlepszy.
ardavis,
12
To daje ["Cat", "Dog", "Mouse", "and", "&"], czego nie chciał OP.
Andrew Grimm,
Świetna rozmowa, Andrew. Zaktualizuję swoją odpowiedź, aby powiedzieć, że nie odpowiedziałem na pytanie Chrisa.
Michael Stalker,
29

Jeśli nie chcesz duplikatu, dlaczego nie użyć operatora unii :

new_array = a | s
Douglas
źródło
1
Przyznanie +1 za niedoceniane, proste i eleganckie rozwiązanie.
Giacomo1968
Oczywiście to odpowiada na pytanie! Pytanie brzmiało: „Chcę scalić tablicę s w tablicę a”
Douglas
Dobre rozwiązanie - ale to zmienia kolejność wyników. Wyniki z sbędą na końcu nowej tablicy.
Hendrik
2
Jednak kolejność elementów nie będzie taka, jakiej chciał PO.
tokland
6
s.inject(a, :<<)

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

Nie zapewnia kolejności, o którą prosiłeś, ale jest to dobry sposób na scalenie dwóch tablic przez dołączenie do jednej.


źródło
Podoba mi się, krótkie i czyste. :)
Nafaa Boutefer
6

Oto rozwiązanie, które umożliwia przeplatanie wielu tablic o różnych rozmiarach (rozwiązanie ogólne):

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"]
Abdo
źródło
2
Miły! Jedno ograniczenie, pierwsza tablica musi być najdłuższa.
Brian Low
@BrianLow świetny połów!
Abdo
5

Nie jest do końca elegancki, ale działa w przypadku tablic o dowolnym rozmiarze:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]
Michael Kohl
źródło
+1 za radzenie sobie z dziwnymi skrajnymi przypadkami, myślę, że i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]byłoby równie ważne.
mu jest za krótkie
Nie byłem pewien, czy PO chce zastępcy andi &, więc wziąłem go dosłownie, jak to możliwe, jednocześnie pozwalając na adowolną długość.
Michael Kohl,
3

Co powiesz na bardziej ogólne rozwiązanie, które działa, nawet jeśli pierwsza tablica nie jest najdłuższa i akceptuje dowolną liczbę tablic?

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

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

 => ["Cat", "and", "Dog", "&", "Mouse"]
Mikrofon
źródło
2

Aby poradzić sobie z sytuacją, w której oba a& snie są tego samego rozmiaru:

a.zip(s).flatten.compact | s
  • .compactusunie, nilgdy ajest większy niżs
  • | sdoda pozostałe elementy od skiedy ajest mniejsze niżs
Shubham Chaudhary
źródło
1

Jednym ze sposobów wykonania przeplotu, a także zagwarantowania, która z nich jest największą tablicą dla metody zip, jest wypełnienie jednej z tablic nildo drugiego rozmiaru tablicy. W ten sposób gwarantujesz również, który element której tablicy będzie na pierwszej pozycji:

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"]
Joao Cunha
źródło
1
Nieco lepiej: preferred_arr.zip(other_arr).flatten | other_arr(bez zasypki zerowej)
Adam Fendley
-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]
David Morrow
źródło
5
przepraszam ... nie zauważyłem konkretnej kolejności, w jakiej chcesz uzyskać wynik. Przepraszamy za próbę pomocy, nie powtórzy się.
David Morrow