Mesclar e intercalar duas matrizes em Ruby

106

Eu tenho o seguinte código:

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

Eu quero mesclar a matriz sem uma matriz aque me daria:

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

Olhando através da documentação Ruby Array e Enumerable, não vejo um método que faça isso.

Existe uma maneira de fazer isso sem iterar em cada array?

Chris Ledet
fonte
a sempre terá 3 elementos e s dois? mais alguns exemplos seriam úteis.
Tóquio,

Respostas:

171

Você pode fazer isso com:

a.zip(s).flatten.compact
DigitalRoss
fonte
4
E se ativer mais de 3 elementos?
Michael Kohl,
116
["a", "b"] .concat (["c", "d"]) # => ["a", "b", "c", "d"]
Leo Romanovsky
30
@Leo, @chuck: se você ler o exemplo, verá que Chris deseja intercalar os elementos, não concatená- los. Essencialmente, ele deseja, [a, s].transposeexceto que as duas linhas não estão em conformidade, deixando #zipcomo a solução óbvia. E eu não acredito que ele quis dizer que ele realmente se importava se aera mutado ... Eu não acho que ele estava comentando sobre uma solução mutada versus funcional, ele estava apenas tentando descrever o padrão.
DigitalRoss
15
+1 por ser a única pessoa que realmente leu a pergunta blummin '! > _ <
Matt Fletcher
5
Mais importante ainda, e se as duas matrizes tiverem comprimentos desiguais? Especialmente se s for o mais longo? Acho que posso assumir com segurança que o exemplo que Chris deu não são os dados reais com os quais está trabalhando. considere: [] .zip [1, 2] => nulo (vai ter dificuldade em chamar #flatten nisso) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (ops, acho que não nos importamos com os últimos elementos na segunda matriz)
hoff2
32

Isso não dará um array de resultado na ordem que Chris pediu, mas se a ordem do array resultante não importa, você pode apenas usar a |= b. Se você não deseja sofrer mutação a, pode escrever a | be atribuir o resultado a uma variável.

Consulte a documentação do conjunto de união para a classe Array em http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Esta resposta pressupõe que você não deseja elementos de array duplicados. Se você quiser permitir elementos duplicados em seu array final, a += bdeve resolver o problema. Novamente, se você não deseja sofrer mutação a, use a + be atribua o resultado a uma variável.

Em resposta a alguns dos comentários nesta página, essas duas soluções funcionarão com arrays de qualquer tamanho.

Michael Stalker
fonte
Este definitivamente parece ser o melhor.
ardavis
11
Isso dá ["Cat", "Dog", "Mouse", "and", "&"], o que não é o que o OP queria.
Andrew Grimm
Excelente chamada, Andrew. Vou atualizar minha resposta para dizer que não respondi à pergunta de Chris.
Michael Stalker
29

Se você não quiser duplicar, por que não usar apenas o operador de união :

new_array = a | s
Douglas
fonte
1
Concedendo um +1 por uma solução subestimada, simples e elegante.
Giacomo1968
Claro que responde à pergunta! A pergunta era: "Eu quero mesclar a matriz s na matriz a"
Douglas
Boa solução - mas isso muda a ordem dos resultados. Os resultados de sestarão no final da nova matriz.
Hendrik,
1
A ordem dos elementos não será a desejada pelo OP.
tokland
6
s.inject(a, :<<)

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

Não fornece a ordem solicitada, mas é uma boa maneira de mesclar duas matrizes anexando a uma.


fonte
Eu gosto disso, curto e limpo. :)
Nafaa Boutefer
6

Aqui está uma solução que permite intercalar várias matrizes de tamanhos diferentes (solução geral):

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
fonte
2
Agradável! Uma limitação, o primeiro array deve ser o mais longo.
Brian Low
@BrianLow grande captura!
Abdo
5

Não é exatamente elegante, mas funciona para matrizes de qualquer tamanho:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]
Michael Kohl
fonte
1 para lidar com casos extremos estranhos, acho i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]que seria igualmente válido.
mu é muito curto
Eu não tinha certeza se OP quer alternar ande &, então eu o tomei o mais literalmente possível, embora permitindo aqualquer comprimento.
Michael Kohl
3

Que tal uma solução mais geral que funcione mesmo se o primeiro array não for o mais longo e aceitar qualquer número de arrays?

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

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

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

Para lidar com a situação em que ae snão são do mesmo tamanho:

a.zip(s).flatten.compact | s
  • .compactirá remover nilquando afor maior ques
  • | sirá adicionar os itens restantes de squando afor menor ques
Shubham Chaudhary
fonte
1

Uma forma de fazer a intercalação e também garantir qual deles é o maior array para o método zip, é preencher um dos arrays com nilaté o outro tamanho de array. Dessa forma, você também garante qual elemento de qual array estará na primeira posição:

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
fonte
1
Um pouco melhor: preferred_arr.zip(other_arr).flatten | other_arr(sem o preenchimento zero)
Adam Fendley
-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]
David Morrow
fonte
5
desculpe ... não notei a ordem específica em que você queria a saída. Desculpas por tentar ajudar, não acontecerá novamente.
David Morrow,