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.