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?


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

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.


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

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.


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"]

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"]

+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"]

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

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"]

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]

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.