Array bidimensionale in Swift


109

Sono così confuso sugli array 2D in Swift. Lasciatemi descrivere passo dopo passo. E mi correggeresti se sbaglio.

Prima di tutto; dichiarazione di un array vuoto:

class test{
    var my2Darr = Int[][]()
}

In secondo luogo riempire la matrice. (ad esempiomy2Darr[i][j] = 0 dove i, j sono variabili di ciclo for)

class test {
    var my2Darr = Int[][]()
    init() {
        for(var i:Int=0;i<10;i++) {
            for(var j:Int=0;j<10;j++) {
                my2Darr[i][j]=18   /*  Is this correct?  */
            }
        }
    }
}

Infine, elemento di modifica di in array

class test {
    var my2Darr = Int[][]()
    init() {
        ....  //same as up code
    }
    func edit(number:Int,index:Int){
        my2Darr[index][index] = number
        // Is this correct? and What if index is bigger
        // than i or j... Can we control that like 
        if (my2Darr[i][j] == nil) { ...  }   */
    }
}

Hai problemi con il tuo approccio?
Alex Wayne

2
Solo così sai, l'intero secondo passaggio può essere ridotto a questo var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))e dovresti davvero passare a una versione beta più recente. Int[][]()non è più una sintassi valida. È stato cambiato in [[Int]]().
Mick MacCallum

1
L'inizializzazione 2D che utilizza valori ripetuti non funzionerà. Tutte le righe punteranno allo stesso sotto-array e quindi non saranno scrivibili in modo univoco.
hotpaw2

Risposte:


228

Definisci array mutabile

// 2 dimensional array of arrays of Ints 
var arr = [[Int]]() 

O:

// 2 dimensional array of arrays of Ints 
var arr: [[Int]] = [] 

O se hai bisogno di un array di dimensioni predefinite (come indicato da @ 0x7fffffff nei commenti):

// 2 dimensional array of arrays of Ints set to 0. Arrays size is 10x5
var arr = Array(count: 3, repeatedValue: Array(count: 2, repeatedValue: 0))

// ...and for Swift 3+:
var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)

Cambia elemento in posizione

arr[0][1] = 18

O

let myVar = 18
arr[0][1] = myVar

Cambia sottoserie

arr[1] = [123, 456, 789] 

O

arr[0] += 234

O

arr[0] += [345, 678]

Se prima di queste modifiche avevi un array 3x2 di 0 (zeri), ora hai:

[
  [0, 0, 234, 345, 678], // 5 elements!
  [123, 456, 789],
  [0, 0]
]

Quindi tieni presente che i sotto-array sono mutabili e puoi ridefinire l'array iniziale che rappresentava la matrice.

Esaminare dimensioni / limiti prima dell'accesso

let a = 0
let b = 1

if arr.count > a && arr[a].count > b {
    println(arr[a][b])
}

Note: stesse regole di markup per array a 3 e N dimensioni.


ok una domanda stupida: come assegniamo quell'array, In C facciamo così: arr [i] [j] = myVar; ma rapidamente quando provo a fare allo stesso modo ho ricevuto questo errore "[([(Int)])]. Il tipo" non ha un membro chiamato "pedice" "
Antiokhos

Se hai arrdefinito come nella risposta, allora myVardovrebbe essere Int, vero?
Keenle

si è int. E grazie mille per la risposta dettagliata .. ora è chiaro: D
Antiokhos

6
In Swift 3, per i copy pasters:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar

1
In Swift 4.2: ad esempio, 3 righe, 2 colonne, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace

27

Dai documenti:

È possibile creare array multidimensionali annidando coppie di parentesi quadre, dove il nome del tipo di base degli elementi è contenuto nella coppia di parentesi quadre più interna. Ad esempio, puoi creare una matrice tridimensionale di numeri interi utilizzando tre serie di parentesi quadre:

var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

Quando si accede agli elementi in un array multidimensionale, l'indice del pedice più a sinistra si riferisce all'elemento in quell'indice nell'array più esterno. Il successivo indice pedice a destra si riferisce all'elemento in quell'indice nell'array che è annidato di un livello. E così via. Ciò significa che nell'esempio precedente, array3D [0] si riferisce a [[1, 2], [3, 4]], array3D [0] [1] si riferisce a [3, 4] e array3D [0] [1 ] [1] si riferisce al valore 4.


17

Rendilo generico Swift 4

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: [T]
    init(rows: Int, columns: Int,defaultValue: T) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: defaultValue, count: rows * columns) as! [T]
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}


var matrix:Matrix<Bool> = Matrix(rows: 1000, columns: 1000,defaultValue:false)

matrix[0,10] = true


print(matrix[0,10])

Ho adattato la tua risposta per creare un array toroidale 2D. Molte grazie! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root

16

Dovresti stare attento quando usi Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Se il valore è un oggetto, che è inizializzato da MyClass(), allora useranno lo stesso riferimento.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)non crea una nuova istanza di MyClassin ogni elemento dell'array. Questo metodo crea solo una MyClassvolta e lo inserisce nell'array.

Ecco un modo sicuro per inizializzare un array multidimensionale.

private var matrix: [[MyClass]] = MyClass.newMatrix()

private static func newMatrix() -> [[MyClass]] {
    var matrix: [[MyClass]] = []

    for i in 0...23 {
        matrix.append( [] )

        for _ in 0...79 {
            matrix[i].append( MyClass() )
        }
    }

    return matrix
}

Salve, possiamo migliorarlo come estensione con il tipo "anyObject"?
Antiokhos

Buon punto sul problema con i tipi di riferimento. Tuttavia, perché scrivi Array(repeating: {value}, could 80)con le parentesi graffe intorno {value}? Ciò creerebbe una serie di chiusure, no?
Duncan C

O è {value}meta-notazione per "qualche valore di tipo AnyObject" (un tipo di riferimento)?
Duncan C

Ho passato quasi un'ora a cercare un bug a causa di questo problema ...
Matheus Weber

13

In Swift 4

var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]

10

Secondo i documenti Apple per swift 4.1 puoi usare questa struttura così facilmente per creare un array 2D:

Collegamento: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html

Esempio di codice:

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

1
Mi piace. Ricorda l'aritmetica del puntatore C. Tuttavia, sarebbe meglio se riscritto usando Generics, quindi si applicherebbe a array bidimensionali di qualsiasi tipo di dati. Del resto, è possibile utilizzare questo approccio per creare array di qualsiasi dimensione arbitraria.
Duncan C

1
@vacawama, fantastico, tranne per il fatto che il tuo array n-dimensionale ha lo stesso problema di tutte le soluzioni che popolano l'array usando Array(repeating:count:). Guarda il commento che ho postato sull'altra tua risposta.
Duncan C

6

Prima di utilizzare array multidimensionali in Swift, considera il loro impatto sulle prestazioni . Nei miei test, l'array appiattito ha funzionato quasi il doppio della versione 2D:

var table = [Int](repeating: 0, count: size * size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        let val = array[row] * array[column]
        // assign
        table[row * size + column] = val
    }
}

Tempo medio di esecuzione per riempire un array 50x50: 82,9 ms

vs.

var table = [[Int]](repeating: [Int](repeating: 0, count: size), count: size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        // assign
        table[row][column] = val
    }
}

Tempo medio di esecuzione per riempire un array 2D 50x50: 135ms

Entrambi gli algoritmi sono O (n ^ 2), quindi la differenza nei tempi di esecuzione è causata dal modo in cui inizializziamo la tabella.

Infine, la cosa peggiore che puoi fare è usare append()per aggiungere nuovi elementi. Questo si è rivelato il più lento nei miei test:

var table = [Int]()    
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        table.append(val)
    }
}

Tempo medio di esecuzione per riempire un array 50x50 utilizzando append (): 2.59s

Conclusione

Evita array multidimensionali e usa l'accesso per indice se la velocità di esecuzione è importante. Gli array 1D sono più performanti, ma il codice potrebbe essere un po 'più difficile da capire.

Puoi eseguire tu stesso i test delle prestazioni dopo aver scaricato il progetto demo dal mio repository GitHub: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground


0

Questo può essere fatto in una semplice riga.

Swift 5

var my2DArray = (0..<4).map { _ in Array(0..<) }

Puoi anche mapparlo a istanze di qualsiasi classe o struttura di tua scelta

struct MyStructCouldBeAClass {
    var x: Int
    var y: Int
}

var my2DArray: [[MyStructCouldBeAClass]] = (0..<2).map { x in
    Array(0..<2).map { MyStructCouldBeAClass(x: x, y: $0)}
}
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.