Fusionar e intercalar dos matrices en Ruby

106

Tengo el siguiente código:

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

Quiero fusionar la matriz sen una matriz aque me daría:

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

Mirando a través de los documentos Ruby Array y Enumerable, no veo un método que logre esto.

¿Hay alguna forma de que pueda hacer esto sin iterar a través de cada matriz?

Chris Ledet
fuente
a siempre tendrá 3 elementos ys dos? algunos ejemplos más serían útiles.
tokland

Respuestas:

171

Puedes hacer eso con:

a.zip(s).flatten.compact
DigitalRoss
fuente
4
¿Y si atiene más de 3 elementos?
Michael Kohl
116
["a", "b"] .concat (["c", "d"]) # => ["a", "b", "c", "d"]
Leo Romanovsky
30
@Leo, @chuck: si lees el ejemplo, verás que Chris quiere intercalar los elementos, no concatenarlos . Esencialmente, quiere, [a, s].transposeexcepto que las dos filas no se ajustan, dejando #zipcomo solución obvia. Y no creo que quisiera decir que realmente le importaba si aestaba mutado ... No creo que estuviera comentando sobre una solución mutada frente a una solución funcional, solo estaba tratando de describir el patrón.
DigitalRoss
15
+1 por ser la única persona que leyó la pregunta blummin '. > _ <
Matt Fletcher
5
Más importante aún, ¿qué pasa si las dos matrices tienen longitudes desiguales? ¿Especialmente si s es el más largo? Creo que puedo asumir con seguridad que el ejemplo que dio Chris no son datos reales con los que está trabajando. considere: [] .zip [1, 2] => nil (va a tener dificultades para llamar a #flatten sobre eso) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (vaya, supongo que no nos importan los últimos elementos de la segunda matriz)
hoff2
32

Esto no dará una matriz de resultados en el orden que pidió Chris, pero si el orden de la matriz resultante no importa, puede usar a |= b. Si no desea mutar a, puede escribir a | by asignar el resultado a una variable.

Consulte la documentación de unión de conjuntos para la clase Array en http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Esta respuesta asume que no desea elementos de matriz duplicados. Si desea permitir elementos duplicados en su matriz final, a += bdebería hacer el truco. Nuevamente, si no desea mutar a, use a + by asigne el resultado a una variable.

En respuesta a algunos de los comentarios de esta página, estas dos soluciones funcionarán con matrices de cualquier tamaño.

Michael Stalker
fuente
Este definitivamente parece ser el mejor.
ardavis
11
Esto da ["Cat", "Dog", "Mouse", "and", "&"], que no es lo que quería el OP.
Andrew Grimm
Excelente llamada, Andrew. Actualizaré mi respuesta para decir que no respondí la pregunta de Chris.
Michael Stalker
29

Si no desea duplicar, ¿por qué no usar el operador de unión ?

new_array = a | s
Douglas
fuente
1
Otorgar un +1 por una solución elegante, simple y subestimada.
Giacomo1968
¡Por supuesto que responde a la pregunta! La pregunta era: "Quiero fusionar la matriz s en la matriz a"
Douglas
Buena solución, pero esto cambia el orden de los resultados. Los resultados de sestarán al final de la nueva matriz.
Hendrik
1
Sin embargo, el orden de los elementos no será el que quería el OP.
tokland
6
s.inject(a, :<<)

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

No le da el orden que pidió, pero es una buena forma de fusionar dos matrices agregando una.


fuente
Me gusta, corto y limpio. :)
Nafaa Boutefer
6

Aquí hay una solución que permite intercalar múltiples matrices de diferentes tamaños (solución general):

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
fuente
2
¡Agradable! Una limitación, la primera matriz debe ser la más larga.
Brian Low
@BrianLow ¡gran captura!
Abdo
5

No es exactamente elegante, pero funciona para matrices de cualquier tamaño:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]
Michael Kohl
fuente
+1 para tratar con casos extremos extraños, creo i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]que sería igualmente válido.
mu es demasiado corto
No estaba seguro de si OP quiere alternar andy &, por lo tanto, lo tomé lo más literalmente posible, mientras permitía acualquier longitud.
Michael Kohl
3

¿Qué tal una solución más general que funcione incluso si la primera matriz no es la más larga y acepta cualquier cantidad de matrices?

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

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

 => ["Cat", "and", "Dog", "&", "Mouse"]
Miguel
fuente
2

Para manejar la situación en la que ambos a& sno son del mismo tamaño:

a.zip(s).flatten.compact | s
  • .compacteliminará nilcuando asea ​​mayor ques
  • | sagregará los elementos restantes de scuando asea ​​menor ques
Shubham Chaudhary
fuente
1

Una forma de hacer la intercalación y también garantizar cuál es la matriz más grande para el método zip, es llenar una de las matrices con nilel tamaño de la otra matriz. De esta manera, también garantiza qué elemento de qué matriz estará en la primera posición:

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
fuente
1
Un poco mejor: preferred_arr.zip(other_arr).flatten | other_arr(sin el relleno nulo)
Adam Fendley
-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]
David Morrow
fuente
5
lo siento ... no noté el orden específico en el que deseaba la salida. Disculpas por intentar ayudar, no volverá a suceder.
David Morrow