Wie fügt man einem anderen Array in Ruby ein Array hinzu und erhält kein mehrdimensionales Ergebnis?

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

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

Ich erwartete

["some","thing","another","thing"]
ncvncvn
quelle
6
Es lohnt sich zu sagen (nicht um dir Kummer zu bereiten, sondern weil es dich immer wieder beißen wird), dass deine Erwartung hier das Problem ist. Ruby-Arrays (im Gegensatz zu beispielsweise Arrays in Perl) werden in solchen Kontexten nicht automatisch abgeflacht. Dies ist kein Fehler, sondern eine Funktion.
Telemachos
3
ri Array@flatten!Warum bekommt diese Frage so viele Stimmen? Das Dokument ist explizit Array#flatten! Flattens self in place. Gibt null zurück, wenn keine Änderungen vorgenommen wurden (dh das Array enthält keine Subarrays.)
yeyo
7
Fragen erhalten positive Stimmen, wenn sie für Benutzer nützlich sind. Die einfachsten Fragen erhalten die meisten positiven Stimmen, weil sie für die meisten Menschen nützlich sind.
Ziggy
@yeyo, denkst du nicht einfach, dass der Abflachungsvorgang kostenlos ist?
Konstantin
@Konstantin op sucht nicht nach Alternativen oder spricht nicht über Leistungsprobleme. Op hat ein Ergebnis erwartet, das er oder sie nicht erhalten hat, weil flatten!es so nicht funktioniert. Schließlich spiegelt die Frage eher ein logisches Problem als ein Optimierungsproblem wider. Weitere Informationen finden Sie in der Antwort von Pilcrow weiter unten.
Yeyo

Antworten:

713

Sie haben eine praktikable Idee, aber das #flatten!ist an der falschen Stelle - es seinen Empfänger verflacht, so dass Sie, um sie nutzen könnten [1, 2, ['foo', 'bar']]in [1,2,'foo','bar'].

Ich vergesse zweifellos einige Ansätze, aber Sie können verketten :

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

oder voranstellen / anhängen :

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

oder Spleiß :

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

oder anhängen und abflachen :

(a1 << a2).flatten!  # a call to #flatten instead would return a new array
Pilger
quelle
17
Gut gemacht, weil ich der einzige war (von 5 kann ich sehen), der tatsächlich darauf hingewiesen hat, was mit dem präsentierten Code nicht stimmte. +1
Mike Woodhouse
53
Durch die Verwendung von Push anstelle von Concat wird die Erstellung eines dritten Arrays vermieden. Dies wird daher für große Arrays bevorzugt.
Phatmann
8
Ich liebe den Push mit dem Sternchen. Sehr elegant.
orourkedd
14
@phatmann Verkettung mit Array#concatordnet kein neues Array zu, Verkettung mit Array#+tut
cbliard
5
Das einzige, was dieser Antwort fehlt, sind Benchmark-Vergleiche der einzelnen Ansätze. +1!
Terra Ashley
205

Sie können einfach den +Operator verwenden!

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]

Sie können alles über die Array-Klasse hier lesen: http://ruby-doc.org/core/classes/Array.html

micmoo
quelle
15
Das Poster wollte wissen, wie man sich auf ein vorhandenes Array konzentriert und kein neues Array erstellt, das die Vereinigung zweier Arrays darstellt.
Phatmann
1
Hinweis: a+= berstellt ein neues Array:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock
1
@kbrock Richtig. Wenn Sie mit großen Arrays arbeiten, sollten Sie sich pushdie von @pilcrow beschriebene Methode ansehen.
Joshua Pinter
2
Denken Sie daran, dass +=ein neues Objekt erstellt wird. In diesem Beispiel wird ein [1, 2].each_with_object([]) { |number, object| object+=number }leeres Array []zurückgegeben
Filip Bartuzi,
1
Der hinzugefügte Gegenstand muss ein Array sein
RousseauAlexandre
66

Der sauberste Ansatz ist die Verwendung der Array # concat- Methode. Es wird kein neues Array erstellt (im Gegensatz zu Array # +, das dasselbe tut, aber ein neues Array erstellt).

Direkt aus den Dokumenten ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

Hängt die Elemente von other_ary an self an.

Damit

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

Array # concat reduziert ein mehrdimensionales Array nicht, wenn es als Argument übergeben wird. Sie müssen das separat behandeln:

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

Zuletzt können Sie unser Corelib-Juwel ( https://github.com/corlewsolutions/corelib ) verwenden, das den Ruby- Kernklassen nützliche Helfer hinzufügt. Insbesondere haben wir eine Array # add_all- Methode, die mehrdimensionale Arrays automatisch reduziert, bevor der Concat ausgeführt wird.

Corlew-Lösungen
quelle
1
Normalerweise möchten Sie Unveränderlichkeit, daher ist das Erstellen eines neuen Arrays eine bessere Idee.
Vasilakisfil
5
"Sie wollen normalerweise Unveränderlichkeit" ist nicht genau. In mehr als 20 Jahren Vollzeit-Softwareentwicklung habe ich täglich mit allen Arten von Arrays und Sammlungen gearbeitet. Manchmal ändern Sie ein vorhandenes Array. Manchmal müssen Sie mit einer neuen Instanz arbeiten.
Corlew Solutions
35

Einfache Methode, die mit Ruby Version> = 2.0 funktioniert, aber nicht mit älteren Versionen:

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]
Ludovic Kuty
quelle
2
@Ikuty Dies ist bei weitem die eleganteste Lösung, die ich gefunden habe. Können Sie bitte erklären, was *hier passiert ?
Abhinay
@Abhinay Der Plat-Operator zerlegt das Array in Elemente und erstellt so ein eindimensionales Array in der letzten Zeile.
Omar Ali
[*a, *b]schlägt für ältere Versionen von Ruby fehl, dh 1.8.7. Und so sehr Ruby Ihnen auch sagen möchte, dass es nicht mehr im Leben ist, RHEL6 wird weiterhin beibehalten, was Ruby 1.8 zu einer bedeutenden Zielversion macht.
Otheus
1
Ich denke nicht, dass dies die -1 rechtfertigt, die diese Antwort erhält. Keine von OP erwähnte Ruby-Version, in der Antwort explizit erwähnte Ruby-Version, also ... möchten Sie abwärtskompatibel mit der Version vor Alpha 0.0.0.0.1 sein? Dies ist eine der guten Lösungen, abhängig von der Ruby-Version
Ludovic Kuty
1
Nur um darauf hinzuweisen, dass diese Antwort dem sehr idiomatischen JavaScript ES6, in dem Sie arbeiten könnten [...array1, ...array2], sehr ähnlich ist. Denken Sie nur daran, dass der splatOperator in Ruby *anstelle von wäre .... Es macht es leichter, sich zu erinnern
sandre89
34

Versuchen Sie dies, es kombiniert Ihre Arrays und entfernt Duplikate

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

array3 = array1|array2

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

Weitere Dokumentation unter "Set Union"

g00se0ne
quelle
Dies ist ein oder, es gibt ein Array ohne doppelte Elemente zurück. Hier ist ein Beispiel dafür, wie es wahrscheinlich nicht das tut, was er verlangt. Die beiden "baz" im ersten Array werden zu einem und die "Leiste". im zweiten Array wird nicht hinzugefügt. array1 = ["foo", "bar", "baz", "baz"] array2 = ["foo1", "bar1", "bar"] array3 = array1 | array2 array3 # => ["foo", "bar "," baz "," foo1 "," bar1 "]
Joshua Cheek
Oder noch besser:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter
33

Hier sind zwei Möglichkeiten: Beachten Sie in diesem Fall, dass die erste Möglichkeit ein neues Array zuweist (übersetzt 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"]
Joshua Cheek
quelle
24
a = ["some", "thing"]
b = ["another", "thing"]

So hängen Sie das Ergebnis ban aund speichern es in a:

a.push(*b)

oder

a += b

In beiden Fällen awird:

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

Im ersteren Fall werden die Elemente von ban das vorhandene aArray angehängt , und im letzteren Fall werden die beiden Arrays miteinander verkettet und das Ergebnis in gespeichert a.

Snibbets
quelle
2
Beachten Sie, dass dies a.push(*b)nicht genau das gleiche ist wie a += b. Ersteres fügt die neuen Elemente dem vorhandenen Array hinzu. Letzterer erstellt ein neues Array mit allen Elementen und weist es zu a. Sie können den Unterschied erkennen, wenn Sie aa = aden Verweis avor dem Anhängen einer Methode speichern und anschließend untersuchen aa. Im ersteren Fall ändert es sich mit dem neuen Wert von aund im letzteren bleibt es unverändert.
Dave Hartnoll
20

(array1 + array2).uniq

Auf diese Weise erhalten Sie zuerst array1-Elemente. Sie erhalten keine Duplikate.

slindsey3000
quelle
9

Bei der Ausarbeitung der Antwort von @ Pilcrow ist die einzige geeignete Antwort für große Arrays concat( +), da sie schnell ist und kein neues Objekt zuweist, das beim Betrieb innerhalb einer Schleife durch Müll gesammelt werden soll.

Hier ist der Benchmark:

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

Ergebnisse:

       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)

Wie Sie mit einem pushWurf sehen können, gibt es einen FEHLER : stack level too deep (SystemStackError)Wenn die Arrays groß genug sind.

juliangonzalez
quelle
8

Die Frage ist im Wesentlichen, wie Arrays in Ruby verkettet werden. Natürlich ist die Antwort zu verwenden concatoder +wie in fast jeder Antwort erwähnt.

Eine natürliche Erweiterung der Frage wäre "wie eine zeilenweise Verkettung von 2D-Arrays in Ruby durchgeführt wird". Als ich "Ruby Concatenate Matrices" googelte, war diese SO-Frage das Top-Ergebnis, also dachte ich, ich würde meine Antwort auf diese (nicht gestellte, aber verwandte) Frage hier der Nachwelt überlassen.


In einigen Anwendungen möchten Sie möglicherweise zwei 2D-Arrays zeilenweise "verketten". Etwas wie,

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

Dies ist so etwas wie das "Erweitern" einer Matrix. Mit dieser Technik habe ich beispielsweise eine einzelne Adjazenzmatrix erstellt, um einen Graphen aus einer Reihe kleinerer Matrizen darzustellen. Ohne diese Technik hätte ich die Komponenten auf eine Weise durchlaufen müssen, die fehleranfällig oder frustrierend gewesen wäre. Ich hätte each_with_indexzum Beispiel eine machen müssen. Stattdessen habe ich Reißverschluss und Abflachung wie folgt kombiniert :

# 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]]
Ziggy
quelle
8

Nur eine andere Art, es zu tun.

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]
Datt
quelle
flattenglättet alles so weit wie möglich, rekursiv. Sogar verschachtelte Arrays. Wenn verschachtelte Arrays enthalten somearrayoder anotherarrayenthalten, werden sie folglich auch abgeflacht. Dies ist eine Nebenwirkung, die normalerweise nicht beabsichtigt ist.
Hagello
5

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

Samg
quelle
Ich weiß nichts über Effizienz, aber das funktioniert für Ruby 1.8. Im Allgemeinen [*a] + [*b]funktioniert
Otheus
Ich denke nicht, dass "another" + "thing"das wie erwartet funktionieren wird.
Alexis Wilke
5

Wenn die neuen Daten ein Array oder ein Skalar sein könnten und Sie verhindern möchten, dass die neuen Daten verschachtelt werden, wenn es sich um ein Array handelt, ist der Splat-Operator fantastisch! Es gibt einen Skalar für einen Skalar und eine entpackte Liste von Argumenten für ein Array zurück.

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 
Sandip Bhattacharya
quelle
4

Ich bin überrascht, dass niemand erwähnt hat reduce, was gut funktioniert, wenn Sie eine Reihe von Arrays haben:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]
ScottJ
quelle
4
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

Dies wird keine Dups entfernen, aber

a|b

Entfernt Dups.

AustintheCleric
quelle
Hinweis: Dadurch werden auch alle inneren Arrays rekursiv abgeflacht.
Mirodinho
2

Ich finde es einfacher, Arrays zu verschieben oder anzuhängen und sie dann wie folgt zu glätten:

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"]
nas
quelle
2

somearray = ["etwas", "Ding"]

anotherarray = ["ein anderes", "Ding"]

Somearray + Anotherarray

Lyle Dickie
quelle