Fusionner et entrelacer deux tableaux dans Ruby

106

J'ai le code suivant:

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

Je veux fusionner le tableau sdans un tableau aqui me donnerait:

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

En parcourant les documents Ruby Array et Enumerable, je ne vois pas une telle méthode qui accomplira cela.

Existe-t-il un moyen de le faire sans parcourir chaque tableau?

Chris Ledet
la source
a aura toujours 3 éléments et s deux? quelques autres exemples seraient utiles.
tokland

Réponses:

171

Vous pouvez le faire avec:

a.zip(s).flatten.compact
DigitalRoss
la source
4
Et si il y aavait plus de 3 éléments?
Michael Kohl
116
["a", "b"] .concat (["c", "d"]) # => ["a", "b", "c", "d"]
Leo Romanovsky
30
@Leo, @chuck: si vous lisez l'exemple, vous verrez que Chris veut entrelacer les éléments, pas les concaténer . Essentiellement, il veut [a, s].transposesauf que les deux lignes ne sont pas conformes, laissant #zipcomme solution évidente. Et je ne pense pas qu'il voulait dire qu'il se souciait vraiment de savoir s'il aétait muté ... Je ne pense pas qu'il parlait du tout d'une solution mutée vs fonctionnelle, il essayait juste de décrire le schéma.
DigitalRoss
15
+1 pour être la seule personne à avoir lu la question blummin '! > _ <
Matt Fletcher
5
Plus important encore, que se passe-t-il si les deux tableaux sont de longueurs inégales? Surtout si s est le plus long? Je pense que je peux supposer sans risque que l'exemple donné par Chris ne soit pas des données réelles avec lesquelles il travaille. considérez: [] .zip [1, 2] => nil (va avoir du mal à appeler #flatten là-dessus) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (oups, je suppose que nous ne nous soucions pas des derniers éléments du 2ème tableau)
hoff2
32

Cela ne donnera pas de tableau de résultats dans l'ordre demandé par Chris, mais si l'ordre du tableau résultant n'a pas d'importance, vous pouvez simplement l'utiliser a |= b. Si vous ne souhaitez pas muter a, vous pouvez écrire a | bet affecter le résultat à une variable.

Consultez la documentation set union pour la classe Array à l' adresse http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Cette réponse suppose que vous ne voulez pas d'éléments de tableau en double. Si vous souhaitez autoriser les éléments en double dans votre tableau final, vous a += bdevriez faire l'affaire. Encore une fois, si vous ne voulez pas muter a, utilisez a + bet affectez le résultat à une variable.

En réponse à certains des commentaires sur cette page, ces deux solutions fonctionneront avec des tableaux de toute taille.

Michael Stalker
la source
Celui-ci semble définitivement être le meilleur.
ardavis
11
Cela donne ["Cat", "Dog", "Mouse", "and", "&"], ce qui n'est pas ce que voulait l'OP.
Andrew Grimm
Excellent appel, Andrew. Je mettrai à jour ma réponse pour dire que je n'ai pas répondu à la question de Chris.
Michael Stalker
29

Si vous ne voulez pas de duplication, pourquoi ne pas simplement utiliser l' opérateur union :

new_array = a | s
Douglas
la source
1
Attribuer un +1 pour une solution sous-estimée, simple et élégante.
Giacomo1968
Bien sûr, cela répond à la question! La question était: "Je veux fusionner les tableaux dans le tableau a"
Douglas
Bonne solution - mais cela change l'ordre des résultats. Les résultats de sseront à la fin du nouveau tableau.
Hendrik
1
L'ordre des éléments ne sera pas ce que le PO voulait, cependant.
tokland
6
s.inject(a, :<<)

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

Cela ne vous donne pas l'ordre que vous avez demandé, mais c'est un bon moyen de fusionner deux tableaux en les ajoutant à l'un.


la source
J'aime ça, court et net. :)
Nafaa Boutefer
6

Voici une solution qui permet d'entrelacer plusieurs tableaux de différentes tailles (solution générale):

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
la source
2
Agréable! Une limitation, le premier tableau doit être le plus long.
Brian Low
@BrianLow bonne prise!
Abdo
5

Ce n'est pas vraiment élégant, mais cela fonctionne pour les tableaux de toutes tailles:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]
Michael Kohl
la source
+1 pour traiter des cas extrêmes étranges, je pense que ce i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]serait tout aussi valable.
mu est trop court le
Je ne savais pas si OP voulait alterner andet &, donc je l'ai pris aussi littéralement que possible, tout en tenant compte ade n'importe quelle longueur.
Michael Kohl
3

Que diriez-vous d'une solution plus générale qui fonctionne même si le premier tableau n'est pas le plus long et accepte n'importe quel nombre de tableaux?

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

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

 => ["Cat", "and", "Dog", "&", "Mouse"]
Mike
la source
2

Pour gérer la situation où les deux a& sne sont pas de la même taille:

a.zip(s).flatten.compact | s
  • .compactsupprimera nilquand aest plus grand ques
  • | sajoutera les éléments restants à partir de squand aest plus petit ques
Shubham Chaudhary
la source
1

Une façon de faire l'entrelacement et de garantir également lequel est le plus grand tableau pour la méthode zip, est de remplir l'un des tableaux avec niljusqu'à l'autre taille de tableau. De cette façon, vous garantissez également quel élément de quel tableau sera en première position:

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
la source
1
Un peu mieux: preferred_arr.zip(other_arr).flatten | other_arr(sans le remblayage nul)
Adam Fendley
-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]
David Morrow
la source
5
désolé ... n'a pas remarqué l'ordre spécifique dans lequel vous vouliez la sortie. Toutes mes excuses pour avoir essayé d'aider, cela ne se reproduira plus.
David Morrow