Unisci e interleave due array in Ruby


106

Ho il codice seguente:

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

Voglio unire l'array sin array ache mi darebbe:

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

Esaminando i documenti Ruby Array ed Enumerable, non vedo un metodo del genere in grado di farlo.

C'è un modo per farlo senza iterare su ogni array?


a avrà sempre 3 elementi es due? alcuni altri esempi sarebbero utili.
— Tokland

Risposte:


171

Puoi farlo con:

a.zip(s).flatten.compact

4
E se aavesse più di 3 elementi?
— Michael Kohl

116
["a", "b"] .concat (["c", "d"]) # => ["a", "b", "c", "d"]
— Leo Romanovsky,

30
@Leo, @chuck: se leggi l'esempio vedrai che Chris vuole intercalare gli elementi, non concatenarli . In sostanza, vuole [a, s].transposetranne che le due file non si conformano, lasciando #zipcome ovvia soluzione. E non credo che intendesse dire che gli importava davvero se aera mutato ... Non credo che stesse commentando affatto una soluzione mutata rispetto a quella funzionale, stava solo cercando di descrivere lo schema.
— DigitalRoss

15
+1 per essere l'unica persona che ha effettivamente letto la domanda blummin '! > _ <
— Matt Fletcher

5
Ancora più importante, cosa succede se i due array hanno lunghezze diverse? Soprattutto se s è quello più lungo? Penso di poter tranquillamente presumere che l'esempio fornito da Chris non sia i dati reali con cui sta lavorando. considera: [] .zip [1, 2] => nil (sarà difficile chiamare #flatten su questo) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (oops, immagino che non ci interessino gli ultimi pochi elementi nel 2 ° array)
— hoff2

32

Questo non darà un array di risultati nell'ordine richiesto da Chris, ma se l'ordine dell'array risultante non ha importanza, puoi semplicemente usare a |= b. Se non vuoi mutare a, puoi scrivere a | be assegnare il risultato a una variabile.

Vedere la documentazione di set union per la classe Array su http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Questa risposta presuppone che tu non voglia duplicare gli elementi dell'array. Se vuoi consentire elementi duplicati nel tuo array finale, a += bdovresti fare il trucco. Di nuovo, se non vuoi cambiare a, usa a + be assegna il risultato a una variabile.

In risposta ad alcuni commenti su questa pagina, queste due soluzioni funzioneranno con array di qualsiasi dimensione.


Questo sembra decisamente il migliore.
— ardavis

11
Questo dà ["Cat", "Dog", "Mouse", "and", "&"], che non è quello che voleva l'OP.
— Andrew Grimm

Ottima chiamata, Andrew. Aggiornerò la mia risposta per dire che non ho risposto alla domanda di Chris.
— Michael Stalker

29

Se non vuoi duplicare, perché non usare semplicemente l' operatore union :

new_array = a | s

1
Assegnazione di un +1 per una soluzione sottovalutata, semplice ed elegante.
— Giacomo1968

Ovviamente risponde alla domanda! La domanda era: "Voglio unire gli array nell'array a"
— Douglas

Buona soluzione, ma questo cambia l'ordine dei risultati. I risultati di ssaranno alla fine del nuovo array.
— Hendrik

1
Tuttavia, l'ordine degli elementi non sarà quello che voleva l'OP.
— Tokland

6
s.inject(a, :<<)

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

Non ti dà l'ordine che hai richiesto, ma è un bel modo per unire due array aggiungendoli a uno.


Mi piace, breve e pulito. :)
— Nafaa Boutefer

6

Ecco una soluzione che consente l'interleaving di più array di dimensioni diverse (soluzione generale):

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
Bello! Una limitazione, il primo array deve essere il più lungo.
— Brian Low

@BrianLow grande cattura!
— Abdo

5

Non è esattamente elegante, ma funziona per array di qualsiasi dimensione:

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

+1 per affrontare casi estremi strani, penso che i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]sarebbe ugualmente valido.
— mu è troppo breve

Non ero sicuro se OP volesse alternarsi ande &, quindi l'ho preso il più letteralmente possibile, pur tenendo conto adi qualsiasi lunghezza.
— Michael Kohl

3

Che ne dici di una soluzione più generale che funzioni anche se il primo array non è il più lungo e accetta un numero qualsiasi di array?

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

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

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

2

Per gestire la situazione in cui entrambi ae snon sono della stessa dimensione:

a.zip(s).flatten.compact | s
  • .compactrimuoverà nilquando aè maggiore dis
  • | saggiungerà gli elementi rimanenti da squando aè minore dis

1

Un modo per eseguire l'interleave e garantire anche quale sia l'array più grande per il metodo zip, è riempire uno degli array con nilfino alla dimensione dell'altro array. In questo modo, garantisci anche quale elemento di quale array sarà in prima posizione:

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
Un po 'meglio: preferred_arr.zip(other_arr).flatten | other_arr(senza il nullo riempimento)
— Adam Fendley

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

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

5
scusa ... non ho notato l'ordine specifico in cui volevi l'output. Mi scuso per aver cercato di aiutare, non succederà più.
— David Morrow,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.