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?


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

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.


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

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.


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

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

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

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

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

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]

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.