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.