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?


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

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.


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

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.


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

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

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

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

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

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]

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.