Come si aggiunge un array a un altro array in Ruby e non si ottiene un risultato multidimensionale?


474
somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

Mi aspettavo

["some","thing","another","thing"]

6
Vale la pena dire (non per darti dolore, ma perché ti morderà ancora e ancora) che le tue aspettative sono il problema qui. Le matrici ruby ​​(diversamente dalle matrici in Perl) non si appiattiscono automaticamente in contesti come questo. Questo non è un bug: è una funzionalità.
Telemaco,

3
ri Array@flatten!Perché questa domanda sta ottenendo così tanti voti? Il documento è esplicito Array#flatten! Appiattisce sul posto. Restituisce nullo se non sono state apportate modifiche (ovvero, l'array non contiene subarrays.)
yeyo

7
Le domande ottengono voti se sono utili agli utenti. Le domande più semplici ottengono il maggior numero di voti perché sono utili alla maggior parte delle persone.
Ziggy

@yeyo, non pensi che l'operazione di appiattimento sia gratuita?
Konstantin

@Konstantin op non è alla ricerca di alternative o non parla di problemi di prestazioni, op si aspettava un risultato che non ha ottenuto perché flatten!non funziona in questo modo. Infine, la domanda riflette un problema logico piuttosto che un problema di ottimizzazione. Vedi di seguito la risposta di Pilcrow per ulteriori informazioni.
yeyo,

Risposte:


714

Hai un'idea praticabile, ma la #flatten!è nel posto sbagliato - si appiattisce il suo ricevitore, così si potrebbe usare per trasformare [1, 2, ['foo', 'bar']]in [1,2,'foo','bar'].

Senza dubbio dimentico alcuni approcci, ma puoi concatenare :

a1.concat a2
a1 + a2              # creates a new array, as does a1 += a2

o anteporre / aggiungere :

a1.push(*a2)         # note the asterisk
a2.unshift(*a1)      # note the asterisk, and that a2 is the receiver

o giuntura :

a1[a1.length, 0] = a2
a1[a1.length..0] = a2
a1.insert(a1.length, *a2)

oppure aggiungi e appiattisci :

(a1 << a2).flatten!  # a call to #flatten instead would return a new array

17
ben fatto per essere l'unico (di 5 che posso vedere) che in realtà ha sottolineato cosa non andava nel codice presentato. +1
Mike Woodhouse,

53
L'uso di push anziché concat evita la creazione di un terzo array, quindi è preferito per array di grandi dimensioni.
Phatmann,

8
Adoro la spinta con l'asterisco. Molto elegante.
orourkedd,

14
@phatmann La concatenazione con Array#concatnon alloca un nuovo array, La concatenazione con Array#+does
cbliard

5
L'unica cosa che manca a questa risposta sono i confronti di riferimento di ciascun approccio. +1!
Terra Ashley,

206

Puoi semplicemente usare l' +operatore!

irb(main):001:0> a = [1,2]
=> [1, 2]
irb(main):002:0> b = [3,4]
=> [3, 4]
irb(main):003:0> a + b
=> [1, 2, 3, 4]

Puoi leggere tutto sulla classe di array qui: http://ruby-doc.org/core/classes/Array.html


15
Il poster voleva sapere come concatenarsi a un array esistente, non creare un nuovo array che era l'unione di due array.
Phatmann,

1
Nota: a+= bcrea un nuovo array:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock

1
@kbrock Correct. Se hai a che fare con array di grandi dimensioni , ti consigliamo di esaminare il pushmetodo descritto da @pilcrow.
Joshua Pinter,

2
ricorda che +=crea un nuovo oggetto. in questo esempio verrà restituita la [1, 2].each_with_object([]) { |number, object| object+=number }matrice vuota[]
Filip Bartuzi,

1
L'articolo aggiunto deve essere un array
RousseauAlexandre

66

L'approccio più pulito consiste nell'utilizzare il metodo Array # concat ; non creerà un nuovo array (diversamente dall'Array # + che farà la stessa cosa ma creerà un nuovo array).

Direttamente dai documenti ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

Aggiunge gli elementi di other_ary a se stesso.

Così

[1,2].concat([3,4])  #=> [1,2,3,4]  

Array # concat non appiattirà un array multidimensionale se viene passato come argomento. Dovrai gestirlo separatamente:

arr= [3,[4,5]]
arr= arr.flatten   #=> [3,4,5]
[1,2].concat(arr)  #=> [1,2,3,4,5]

Infine, puoi usare la nostra gemma corelib ( https://github.com/corlewsolutions/corelib ) che aggiunge utili aiutanti alle classi principali di Ruby. In particolare abbiamo un metodo Array # add_all che appiattirà automaticamente gli array multidimensionali prima di eseguire il concat.


1
Di solito si desidera l'immutabilità, quindi la creazione di un nuovo array è un'idea migliore.
vasilakisfil,

5
"Di solito vuoi l'immutabilità" non è preciso. In oltre 20 anni di sviluppo software a tempo pieno, ho lavorato quotidianamente con tutti i tipi di array e raccolte. A volte si modifica un array esistente in atto. A volte è necessario lavorare con una nuova istanza.
Corlew Solutions,

35

Metodo semplice che funziona con la versione di Ruby> = 2.0 ma non con le versioni precedenti:

irb(main):001:0> a=[1,2]
=> [1, 2]
irb(main):003:0> b=[3,4]
=> [3, 4]
irb(main):002:0> c=[5,6]
=> [5, 6]
irb(main):004:0> [*a,*b,*c]
=> [1, 2, 3, 4, 5, 6]

2
@Ikuty Questa è di gran lunga la soluzione più elegante che ho trovato, puoi spiegarci cosa sta succedendo *qui?
Abhinay,

@Abhinay l'operatore plat esplode l'array in elementi creando così un array monodimensionale nell'ultima riga.
Omar Ali,

[*a, *b]fallisce per le versioni precedenti di ruby, ovvero 1.8.7. E per quanto Ruby voglia dirti che è fuori dalla vita, RHEL6 è ancora mantenuto, rendendo Ruby 1.8 una versione di destinazione molto significativa.
Otheus,

1
Non penso che giustifichi il -1 che ottiene questa risposta. Nessuna versione ruby ​​menzionata da OP, la versione ruby ​​menzionata esplicitamente nella risposta, quindi ... vuoi essere retrocompatibile con la versione pre alpha 0.0.0.0.1? Questa è una delle buone soluzioni, a seconda della versione rubino
Ludovic Kuty

1
Solo per indicare che questa risposta è molto "simile" al molto idiomatico JavaScript ES6 in cui si potrebbe fare [...array1, ...array2], ricordando solo che l' splatoperatore in ruby ​​sarebbe *invece .... Rende più facile da ricordare
sandre89,

34

Prova questo, combinerà i tuoi array rimuovendo i duplicati

array1 = ["foo", "bar"]
array2 = ["foo1", "bar1"]

array3 = array1|array2

http://www.ruby-doc.org/core/classes/Array.html

Ulteriore documentazione guarda "Set Union"


Questo è un o, restituisce un array senza elementi duplicati, ecco un esempio di come probabilmente non fa quello che sta chiedendo, i due "baz" nel primo array vengono trasformati in uno e la "barra" nel secondo array non viene aggiunto. array1 = ["foo", "bar", "baz", "baz"] array2 = ["foo1", "bar1", "bar"] array3 = array1 | array2 array3 # => ["foo", "bar "," baz "," foo1 "," bar1 "]
Joshua Cheek,

O meglio:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter

33

Ecco due modi, nota in questo caso che il primo modo assegna un nuovo array (si traduce in somearray = somearray + anotherarray)

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray += anotherarray # => ["some", "thing", "another", "thing"]

somearray = ["some", "thing"]
somearray.concat anotherarray # => ["some", "thing", "another", "thing"]

25
a = ["some", "thing"]
b = ["another", "thing"]

Per aggiungere balla ae memorizzare il risultato in a:

a.push(*b)

o

a += b

In entrambi i casi, adiventa:

["some", "thing", "another", "thing"]

ma nel primo caso, gli elementi di bvengono aggiunti aall'array esistente e nel secondo caso i due array vengono concatenati insieme e il risultato viene archiviato a.


2
Nota che a.push(*b)non è esattamente lo stesso di a += b. Il primo aggiunge i nuovi elementi all'array esistente; quest'ultimo crea un nuovo array con tutti gli elementi e lo assegna a a. Puoi vedere la differenza se fai qualcosa come aa = asalvare l'arbitro aprima di entrambi i metodi di accodamento e poi esaminare in aaseguito. Nel primo caso, cambia con il nuovo valore di ae nel secondo rimane invariato.
Dave Hartnoll,

20

(array1 + array2).uniq

In questo modo si ottengono prima gli elementi array1. Non otterrai duplicati.


9

Elaborando la risposta di @ Pilcrow l'unica risposta adatta per array di grandi dimensioni è concat( +) poiché è veloce e non alloca un nuovo oggetto da raccogliere in modo inutile quando si opera all'interno di un ciclo.

Ecco il punto di riferimento:

require 'benchmark'

huge_ary_1 = Array.new(1_000_000) { rand(5_000_000..30_000_00) }

huge_ary_2 = Array.new(1_000_000) { rand(35_000_000..55_000_00) }

Benchmark.bm do |bm|
  p '-------------------CONCAT ----------------'
  bm.report { huge_ary_1.concat(huge_ary_2) }

  p '------------------- PUSH ----------------'
  bm.report { huge_ary_1.push(*huge_ary_2)  }
end

risultati:

       user     system      total        real
"-------------------CONCAT ----------------"
  0.000000   0.000000   0.000000 (  0.009388)
"------------------- PUSH ----------------"
  example/array_concat_vs_push.rb:13:in `block (2 levels) in <main>': stack level too deep (SystemStackError)

Come puoi vedere, usa pushun ERRORE : stack level too deep (SystemStackError)quando le matrici sono abbastanza grandi.


8

La domanda, in sostanza, è "come concatenare le matrici in Ruby". Naturalmente la risposta è usare concato +come menzionato in quasi ogni risposta.

Un'estensione naturale della domanda sarebbe "come eseguire la concatenazione di array 2D in Ruby". Quando ho cercato su Google "matrici concatenate di rubini", questa domanda SO è stata il miglior risultato, quindi ho pensato di lasciare la mia risposta a quella domanda (non richiesta ma correlata) qui per i posteri.


In alcune applicazioni potresti voler "concatenare" due array 2D in ordine di riga. Qualcosa di simile a,

[[a, b], | [[x],    [[a, b, x],
 [c, d]] |  [y]] =>  [c, d, y]]

Questo è qualcosa come "aumentare" una matrice. Ad esempio, ho usato questa tecnica per creare una singola matrice di adiacenza per rappresentare un grafico da un gruppo di matrici più piccole. Senza questa tecnica avrei dovuto scorrere i componenti in un modo che avrebbe potuto essere soggetto a errori o frustrante a cui pensare. Potrei aver dovuto fare un each_with_index, per esempio. Invece ho combinato zip e appiattire come segue,

# given two multi-dimensional arrays that you want to concatenate row-wise
m1 = [[:a, :b], [:c, :d]]
m2 = [[:x], [:y]]

m1m2 = m1.zip(m2).map(&:flatten)
# => [[:a, :b, :x], [:c, :d, :y]]

8

Solo un altro modo di farlo.

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]

flattenappiattisce tutto il più possibile, in modo ricorsivo. Anche array nidificati. Di conseguenza, se somearrayo anotherarraycontengono array nidificati, anche questi vengono appiattiti. Questo è un effetto collaterale che di solito non è previsto.
hagello,

5

["some", "thing"] + ["another" + "thing"]


Non conosco l'efficienza, ma questo funziona per Ruby 1.8. In generale, [*a] + [*b]funziona
Otheus

Non penso che "another" + "thing"funzionerà come previsto.
Alexis Wilke,

5

Se i nuovi dati potrebbero essere una matrice o uno scalare e si desidera impedire che i nuovi dati vengano nidificati se fosse una matrice, l'operatore splat è fantastico! Restituisce uno scalare per uno scalare e un elenco decompresso di argomenti per un array.

1.9.3-p551 :020 > a = [1, 2]
 => [1, 2] 
1.9.3-p551 :021 > b = [3, 4]
 => [3, 4] 
1.9.3-p551 :022 > c = 5
 => 5 
1.9.3-p551 :023 > a.object_id
 => 6617020 
1.9.3-p551 :024 > a.push *b
 => [1, 2, 3, 4] 
1.9.3-p551 :025 > a.object_id
 => 6617020 
1.9.3-p551 :026 > a.push *c
 => [1, 2, 3, 4, 5] 
1.9.3-p551 :027 > a.object_id
 => 6617020 

4

Sono sorpreso che nessuno abbia menzionato reduce, il che funziona bene quando si dispone di una serie di array:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]

4
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

Questo non rimuoverà i duplicati, ma

a|b

rimuove i duplicati.


Nota: questo appiattisce ricorsivamente anche tutti gli array interni.
Mirodinho,

2

Trovo più facile spingere o aggiungere array e poi appiattirli in posizione, in questo modo:

somearray = ["some", "thing"]
anotherarray = ["another", "thing"]
somearray.push anotherarray # => ["some", "thing", ["another", "thing"]]
#or
somearray << anotherarray # => ["some", "thing", ["another", "thing"]]
somearray.flatten!  # => ["some", "thing", "another", "thing"]
somearray # => ["some", "thing", "another", "thing"]

2

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray + anotherarray

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.