Erstellen Sie zweidimensionale Arrays und greifen Sie in Ruby auf Sub-Arrays zu

71

Ich frage mich, ob es eine Möglichkeit gibt, ein zweidimensionales Array zu erstellen und schnell auf ein horizontales oder vertikales Sub-Array darin zuzugreifen.

Ich glaube, wir können im folgenden Fall auf ein horizontales Subarray zugreifen:

x = Array.new(10) { Array.new(20) }

x[6][3..8] = 'something'

Aber soweit ich weiß, können wir nicht so darauf zugreifen:

x[3..8][6]

Wie kann ich dieses Limit vermeiden oder hacken?

gmile
quelle
1
Ich denke, es x[3..8].each{|a|a[6] = 'something'}ist zu hässlich für dich.
Komplistische

Antworten:

99

Es gibt einige Probleme mit der zweidimensionalen ArraysImplementierung.

a= [[1,2],[3,4]]
a[0][2]= 5 # works
a[2][0]= 6 # error

Hash wie Array

Ich bevorzuge es Hashesfür mehrdimensionaleArrays

a= Hash.new
a[[1,2]]= 23
a[[5,6]]= 42

Dies hat den Vorteil, dass Sie keine Spalten oder Zeilen manuell erstellen müssen. Das Einfügen in Hashes ist fast O (1) , daher gibt es hier keinen Nachteil, solange Ihr Hashnicht zu groß wird.

Sie können sogar einen Standardwert für alle nicht angegebenen Elemente festlegen

a= Hash.new(0)

Nun also darüber, wie man Subarrays bekommt

(3..5).to_a.product([2]).collect { |index| a[index] }
[2].product((3..5).to_a).collect { |index| a[index] }

(a..b).to_aläuft in O (n). Das Abrufen eines Elements von a Hashist fast O (1), daher läuft die Sammlung in fast O (n). Es gibt keine Möglichkeit, es schneller als O (n) zu machen, da das Kopieren von n Elementen immer O (n) ist.

Hasheskann Probleme haben, wenn sie zu groß werden. Ich würde also zweimal darüber nachdenken, eine solche Multidimensionale zu implementieren Array, wenn ich wüsste, dass meine Datenmenge immer größer wird.

johannes
quelle
4
Dies ist alt, aber a[2][0]ein Fehler, weil das 3. Element noch nicht erstellt wurde? Bedeutung a[1][0]=6würde funktionieren? Ich weiß, das ist alt ... ich schaue gerade Ruby an.
Vol7ron
Ich glaube du bist richtig @ vol7tron und a[1][0] = 6funktioniert. Sie können die dritte Zeile mit einem [2] erstellen, aber Sie können erst nach dem Erstellen einen Index erstellen. ZB a[2] = []gefolgt von a[2][0] = 6wird funktionieren.
Salbei
1
Was meinst du mit [...] almost O(1)[...]?
Ich
Obwohl es eine interessante Lösung ist, werden Sie viel Platz für die Erstellung von Arrays als Schlüssel verwenden. Für Zahlen gibt es keine Speicherzuordnung, für Arrays jedoch.
Sahilbathla
ordentlicher Trick, es hat jedoch einen Nachteil: Sie können die Größe der Matrix nicht durch Betrachten ableiten. Wenn alle Ihre Werte Null sind, können Sie die Größe des mehrdimensionalen Arrays nicht ermitteln
Don Giulio
36
rows, cols = x,y  # your values
grid = Array.new(rows) { Array.new(cols) }

Was den Zugriff auf Elemente betrifft, ist dieser Artikel ziemlich gut geeignet, um ein Array Schritt für Schritt wie folgt zu kapseln:

Wie man Array rubin

shawnjan
quelle
30

Sie haben Ihr eigentliches Ziel nicht angegeben, aber vielleicht kann dies helfen:

require 'matrix'  # bundled with Ruby
m = Matrix[
 [1, 2, 3],
 [4, 5, 6]
]

m.column(0) # ==> Vector[1, 4]

(und Vektoren wirken wie Arrays)

oder mit einer ähnlichen Notation, wie Sie es wünschen:

m.minor(0..1, 2..2) # => Matrix[[3], [6]]
Marc-André Lafortune
quelle
Wenn Sie zusätzliche Funktionen benötigen oder die x[6][3..8]Notation bevorzugen , können Sie sie jederzeit unterordnen Matrixund erweitern.
Bta
6
MatrixNachdem ich diese Antwort gelesen hatte, fing ich an zu verwenden , und es war großartig. Es gibt jedoch eine große Einschränkung, die Sie besser berücksichtigen sollten, bevor MatrixSie zu wechseln - sie sind unveränderlich. Es gibt also keine Möglichkeit, ein einzelnes Element zu ändern, dhm(0, 0) = 0 # => error
Gregoltsov
2
@ GregoryGoltsov: +1 an Marc-André zum Schreiben Matrix. Ihre Unveränderlichkeit würde ich nicht als Einschränkung, sondern als Merkmal bezeichnen. Anscheinend macht Marc-Anré sich nicht nur das Leben leichter, sondern präsentiert Matrizen auch als Verallgemeinerung von Zahlen.
Boris Stitnicky
1
@ BorisStitnicky: Der ursprüngliche Autor der Bibliothek ist Keiju Ishitsuka, nicht ich. Ich muss mich auch wirklich darum kümmern, Matrizen für die nächste Version veränderbar zu machen :-)
Marc-André Lafortune
1
@ Marc-AndréLafortune: Ich habe die Unveränderlichkeit in meiner YPetri :: Simulation-Klasse so sehr geschätzt , dass ich sogar die Eingaben betrogen habe . Die Unveränderlichkeit der Matrix, die ich darin verwende, ist praktisch. Bitte, wenn Sie die Matrizen wandelbar machen, stellen Sie sicher , dass sie entweder eine separate Unterklasse sind ( OpenMatrixzum Beispiel, wie OpenStructund Struct), oder der Benutzer hat zunächst so etwas wie zu tun , Matrix#openoder , #unlockoder was.
Boris Stitnicky
11

Hier ist ein 3D-Array-Fall

class Array3D
   def initialize(d1,d2,d3)
    @data = Array.new(d1) { Array.new(d2) { Array.new(d3) } }
   end

  def [](x, y, z)
    @data[x][y][z]
  end

  def []=(x, y, z, value)
    @data[x][y][z] = value
  end
end

Sie können wie jedes andere Ruby-Array auf Unterabschnitte jedes Arrays zugreifen. @data [0..2] [3..5] [8..10] = 0 usw.

Mark Essel
quelle
6

x.transpose[6][3..8]oder x[3..8].map {|r| r [6]}würde geben was du willst.

Beispiel:

a = [ [1,  2,  3,  4,  5],
      [6,  7,  8,  9,  10],
      [11, 12, 13, 14, 15],
      [21, 22, 23, 24, 25]
    ]

#a[1..2][2]  -> [8,13]
puts a.transpose[2][1..2].inspect   # [8,13]
puts a[1..2].map {|r| r[2]}.inspect  # [8,13]
pierrotlefou
quelle
Oh, Array geerbt transpose? Großartig!
Meilen
1
Das Problem dabei ist: Die Transponierung ist O (n * m), aber das Abrufen eines Subarrays in eine Richtung kann in O (n + m) erfolgen
johannes
Verwenden von collectstatt mapfügt hier ein wenig Klarheit hinzu.
Glenn Jackman
Nach meinem Verständnis sind Karte und Sammeln gleich. Es ist nur der Name, den Sie für diese Aufgabe bevorzugen.
Johannes
2

Ich bin mir ziemlich sicher, dass dies sehr einfach sein kann

2.0.0p247 :032 > list = Array.new(5)

 => [nil, nil, nil, nil, nil] 

2.0.0p247 :033 > list.map!{ |x| x = [0] }

 => [[0], [0], [0], [0], [0]] 

2.0.0p247 :034 > list[0][0]

  => 0
vladCovaliov
quelle
Sie müssen nicht zuweisen, xwie [0]sowieso zurückgegeben wird
Simone
Notiz Array.newkann einen Block direkt nehmen, keine Notwendigkeit für map: Array.new(5) {|x| x = [0] } => [[0], [0], [0], [0], [0]]oder noch einfacher:Array.new(5,[0]) => [[0], [0], [0], [0], [0]]
Alex Moore-Niemi
0
a = Array.new(Array.new(4))

0.upto(a.length-1) do |i|
  0.upto(a.length-1) do |j|
    a[i[j]] = 1
  end
end

0.upto(a.length-1) do |i|
  0.upto(a.length-1) do |j|
    print a[i[j]] = 1 #It's not a[i][j], but a[i[j]]
  end
  puts "\n"
end
anon
quelle
-2

Hier ist die einfache Version

 #one
 a = [[0]*10]*10

 #two
row, col = 10, 10
a = [[0]*row]*col
Mohan
quelle
8
Das in diesem Ansatz erstellte Array bezieht sich auf dasselbe 1-dimensionale Array, dh das Ändern eines Werts wirkt sich auf die anderen aus, z. B. wird durch eine [0] [0] = 1 auch eine [1] [0] zu 1.
Kevin C.
-3

Hier ist eine einfache Möglichkeit, ein "2D" -Array zu erstellen.

2.1.1 :004 > m=Array.new(3,Array.new(3,true))

=> [[true, true, true], [true, true, true], [true, true, true]]
amcritchie
quelle
4
Dadurch wird ein Array (3) mit Verweisen auf ein einzelnes neues Array (3) erstellt. Das sind insgesamt 2 Arrays und 1 Boolesche Objekte. Ich denke du willst so etwas m=Array.new(3){Array.new(3, true)}. Das würde Ihnen 4 Arrays und 3 Boolesche Objekte geben.
Komplistische