Array dua dimensi di Swift

109

Saya jadi bingung tentang array 2D di Swift. Izinkan saya menjelaskan langkah demi langkah. Dan bisakah Anda mengoreksi saya jika saya salah.

Pertama-tama; deklarasi array kosong:

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

Kedua, isi larik. (seperti di my2Darr[i][j] = 0mana i, j adalah variabel loop-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?  */
            }
        }
    }
}

Dan Terakhir, Mengedit elemen 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) { ...  }   */
    }
}
Antiokhos
sumber
Apakah Anda mengalami masalah dengan pendekatan Anda?
Alex Wayne
2
Asal tahu saja, seluruh langkah kedua Anda dapat dikurangi menjadi ini var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))Dan Anda harus benar-benar meningkatkan ke versi beta yang lebih baru. Int[][]()sintaks tidak lagi valid. Sudah diubah menjadi [[Int]]().
Mick MacCallum
1
2D init yang menggunakan nilai berulang tidak akan berfungsi. Semua baris akan mengarah ke sub-larik yang sama, sehingga tidak ada yang dapat ditulis secara unik.
hotpaw2

Jawaban:

228

Tentukan larik yang bisa berubah

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

ATAU:

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

ATAU jika Anda membutuhkan larik dengan ukuran yang telah ditentukan (seperti yang disebutkan oleh @ 0x7fffffff di komentar):

// 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)

Ubah elemen di posisi

arr[0][1] = 18

ATAU

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

Ubah sub array

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

ATAU

arr[0] += 234

ATAU

arr[0] += [345, 678]

Jika Anda memiliki larik 3x2 berisi 0 (nol) sebelum perubahan ini, sekarang Anda memiliki:

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

Jadi ketahuilah bahwa sub larik bisa berubah dan Anda bisa mendefinisikan kembali larik awal yang mewakili matriks.

Periksa ukuran / batasan sebelum mengakses

let a = 0
let b = 1

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

Catatan: Aturan markup yang sama untuk array dimensi 3 dan N.

Keenle
sumber
ok satu pertanyaan bodoh: bagaimana kita menetapkan array itu, Dalam C kita lakukan seperti itu: arr [i] [j] = myVar; tetapi dengan cepat ketika saya mencoba melakukan cara yang sama saya mendapat kesalahan ini "'[([(Int)])]. Ketik' tidak memiliki anggota yang bernama 'subscript'"
Antiokhos
Jika sudah di arrdefinisikan seperti pada jawaban maka myVarharus Int, bukan?
Keenle
ya itu int. Dan terima kasih banyak atas jawaban yang rinci .. sekarang jelas: D
Antiokhos
6
Di Swift 3, untuk copy pasters:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar
1
Di Swift 4.2: misalnya, 3 baris, 2 kolom, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace
27

Dari dokumen:

Anda dapat membuat larik multidimensi dengan memasang pasangan tanda kurung siku, di mana nama jenis dasar elemen dimuat dalam pasangan paling dalam dari tanda kurung siku. Misalnya, Anda dapat membuat larik tiga dimensi dari bilangan bulat menggunakan tiga kumpulan tanda kurung siku:

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

Saat mengakses elemen dalam larik multidimensi, indeks subskrip paling kiri merujuk ke elemen pada indeks tersebut di larik terluar. Indeks subskrip berikutnya di sebelah kanan mengacu pada elemen pada indeks itu dalam larik yang bertingkat satu tingkat. Dan seterusnya. Ini berarti bahwa pada contoh di atas, array3D [0] mengacu pada [[1, 2], [3, 4]], array3D [0] [1] mengacu pada [3, 4], dan array3D [0] [1 ] [1] mengacu pada nilai 4.

Woodstock
sumber
17

Jadikan Generic 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])
dimo hamdy
sumber
Saya mengadaptasi jawaban Anda untuk membuat array toroidal 2D. Terima kasih banyak! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root
16

Anda harus berhati-hati saat menggunakan Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Jika nilainya adalah sebuah objek, yang diinisialisasi oleh MyClass(), maka mereka akan menggunakan referensi yang sama.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)tidak membuat instance baru MyClassdi setiap elemen array. Metode ini hanya membuat MyClasssekali dan memasukkannya ke dalam array.

Berikut cara yang aman untuk menginisialisasi array multidimensi.

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
}
Kimi Chiu
sumber
Hai, bisakah kami meningkatkannya sebagai ekstensi dengan jenis "anyObject"?
Antiokhos
Poin bagus tentang masalah dengan tipe referensi. Namun, mengapa Anda menulis Array(repeating: {value}, could 80)dengan kawat gigi {value}? Itu akan menciptakan serangkaian penutupan, bukan?
Duncan C
Atau apakah {value}meta-notation untuk "beberapa nilai tipe AnyObject" (tipe referensi)?
Duncan C
Saya menghabiskan hampir satu jam mencari bug karena masalah ini ...
Matheus Weber
13

Di Swift 4

var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]
Obat kumur Ankit
sumber
10

Menurut dokumen Apple untuk swift 4.1 Anda dapat menggunakan struct ini dengan mudah untuk membuat array 2D:

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

Contoh kode:

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
        }
    }
}
Keyhan Kamangar
sumber
1
Saya suka itu. Ini mengingatkan pada aritmatika penunjuk C. Akan lebih baik jika ditulis ulang menggunakan Generik, jadi ini akan berlaku untuk array 2 dimensi dari tipe data apa pun. Dalam hal ini, Anda dapat menggunakan pendekatan ini untuk membuat array dimensi arbitrer apa pun.
Duncan C
1
@vacawama, keren, kecuali larik n-dimensi Anda memiliki masalah yang sama dengan semua solusi yang mengisi larik menggunakan Array(repeating:count:). Lihat komentar yang saya posting ke jawaban Anda yang lain.
Duncan C
6

Sebelum menggunakan array multidimensi di Swift, pertimbangkan dampaknya terhadap kinerja . Dalam pengujian saya, array yang diratakan berkinerja hampir 2x lebih baik daripada versi 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
    }
}

Waktu eksekusi rata-rata untuk mengisi Array 50x50: 82.9ms

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

Waktu eksekusi rata-rata untuk mengisi Array 2D 50x50: 135ms

Kedua algoritma tersebut adalah O (n ^ 2), jadi perbedaan waktu eksekusi disebabkan oleh cara kita menginisialisasi tabel.

Terakhir, hal terburuk yang dapat Anda lakukan adalah append()menambahkan elemen baru. Itu terbukti paling lambat dalam pengujian saya:

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

Waktu eksekusi rata-rata untuk mengisi Array 50x50 menggunakan append (): 2.59 d

Kesimpulan

Hindari array multidimensi dan gunakan akses menurut indeks jika kecepatan eksekusi penting. Array 1D lebih berkinerja, tetapi kode Anda mungkin sedikit lebih sulit untuk dipahami.

Anda dapat menjalankan tes kinerja sendiri setelah mengunduh proyek demo dari repo GitHub saya: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground

Karoly Nyisztor
sumber
0

Ini bisa dilakukan dalam satu baris sederhana.

Cepat 5

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

Anda juga dapat memetakannya ke instance dari kelas atau struct pilihan Anda

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)}
}
pimisi
sumber