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?


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

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.


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

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.


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

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

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

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

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

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]

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,
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.