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?


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.


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


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

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.


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]

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

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

-2

Hier ist die einfache Version

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

 #two
row, col = 10, 10
a = [[0]*row]*col

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

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.