Ratakan Array of Arrays di Swift

153

Apakah ada rekan di Swift flattendi Scala, Xtend, Groovy, Ruby dan co?

var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatten() // shall deliver [1,2,3,4,5,6,7,8,9] 

Tentu saja saya bisa menggunakan pengurangan untuk itu tapi itu agak menyebalkan

var flattened = aofa.reduce(Int[]()){
    a,i in var b : Int[] = a
    b.extend(i)
    return b
}
Christian Dietrich
sumber
bukankah itu seperti menggunakan add object dari sebuah array?
Pham Hoan
Saya belum melihat ke Swift itu sendiri tetapi di Haskell dan F # itu `concat` - jadi mungkin terlihat sesuatu yang dinamai seperti ini? - Saya agak yakin bahwa ini ada di suatu tempat (sebagian besar bahasa FP tahu tentang monad dan ini adalah ikatan Daftar)
Carsten
ya di haskell sebenarnya disebut concat.
Christian Dietrich
Anda harus menerima dan memeriksa jawaban orang lain .
Rob

Jawaban:

460

Swift> = 3.0

reduce:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = numbers.reduce([], +)

flatMap:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = numbers.flatMap { $0 }

joined:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let joined = Array(numbers.joined())

masukkan deskripsi gambar di sini

andreschneider
sumber
3
Hanya untuk menyatakan ini secara lebih umum, flatMaptersedia pada Swift 1.2.
Mick MacCallum
3
Apa perbedaan antara joined(secara resmi dikenal sebagai flatten) dengan flatMap? Apakah saat flatMapbergabung, ia juga dapat memetakan / mengubah sesuatu. tapi di sini, dalam contoh kita benar-benar tidak perlu yaitu kita kembali$0
Honey
6
@Dschee flatMapakan baik meratakan array 2D ke dalam array 1D atau menghapus nilnilai-nilai, tidak keduanya. Ini menentukan apa yang harus dilakukan berdasarkan apakah larik tingkat pertama Elementadalah larik atau opsional — jadi jika Anda meneruskannya ke larik 2D opsional (misalnya [[Int?]]), ia akan memilih untuk meratakannya ke 1D (misalnya [Int?]) . Untuk meratakan ke 1-D dan menghapus nol tingkat 2, Anda harus melakukannya array.flatMap { $0 }.flatMap { $0 }. Dengan kata lain, perataan dimensi ekuiv Array(array.joined())dan “perataan” penghilangan nihil sama dengan array.filter{ $0 != nil }.map{ $0! }.
Slipp D. Thompson
1
@Warpling flatMapmasih sesuai untuk penggunaan yang dijelaskan dalam pertanyaan (meratakan array 2D ke 1D). compactMapsecara eksplisit untuk menghapus nilitem dari urutan, seperti yang flatMappernah dilakukan oleh varian .
Jim Dovey
1
@mohamadrezakoohkan itu benar. Karena array Anda bertipe [[Any]], a flatMapcukup mengubahnya menjadi tipe [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). Dan jika kita akan berlaku flatMaplagi, kami akan bertindak pada `Setiap? type, dimana kompilator tidak tahu lagi apakah itu nilai sederhana atau array itu sendiri.
andreschneider
31

Dalam Swift perpustakaan standar ada joinedfungsi yang diimplementasikan untuk semua jenis sesuai dengan Sequenceprotokol (atau flattenpada SequenceTypesebelum Swift 3), yang meliputi Array:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = Array(numbers.joined())

Dalam kasus tertentu, penggunaan dari joined()dapat bermanfaat karena mengembalikan koleksi yang lazy daripada array baru, tetapi selalu dapat diubah menjadi array saat diteruskan ke Array()penginisialisasi seperti pada contoh di atas.

Max Desiatov
sumber
@chrisco bisakah Anda jelaskan bagaimana jawaban saya salah dan apa kriteria untuk "jawaban benar yang paling sederhana"? Bisakah Anda juga memberi tahu bagaimana menghapus jawaban dapat memengaruhi pertanyaan dengan cara apa pun?
Max Desiatov
Coba jalankan cuplikan Anda terlebih dahulu - menurut Anda apa fungsinya? Apa yang sebenarnya dilakukannya? Apa pertanyaan aslinya? Apakah jawaban Anda benar? Jika tidak, alangkah lebih baik di hapus untuk meningkatkan kejelasan postingan ini. Saya telah melakukan hal yang sama dengan jawaban saya sendiri yang salah.
Chris Conover
1
@chrisco terima kasih banyak atas saran Anda, tetapi saya menjalankan cuplikan sebelum mempostingnya di mana saja. Dan jawaban saya benar karena mengembalikan hasil yang sama persis seperti yang diminta OP dan menggunakan lebih sedikit kode untuk itu. Saya akui bahwa jawaban asli saya adalah mengembalikan koleksi malas, bukan array, meskipun tidak ada batasan dalam pertanyaan. Saya masih tidak berpikir bahwa penghapusan jawaban yang benar meningkatkan kualitas pertanyaan dengan cara apa pun.
Max Desiatov
Ini adalah poin saya - bahwa ketika pengujian / mencetak output, Anda mendapatkan sebuah array dari array: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Poin Anda valid meskipun Anda dapat mengaksesnya seperti array datar, jadi CustomStringConvertableimplementasinya tampaknya menyesatkan. Cuplikan kode Anda pernah dan masih melewatkan pengujian.
Chris Conover
1
Pada swift 3.0, flatten()telah diubah namanya menjadijoined()
Mr. Xcoder
18

Cepat 4.x / 5.x

Hanya untuk menambah sedikit kerumitan pada array, jika ada array yang berisi array, maka flatMapsebenarnya akan gagal.

Misalkan array tersebut

var array:[Any] = [1,2,[[3,4],[5,6,[7]]],8]

Apa flatMapatau compactMappengembaliannya adalah:

array.compactMap({$0})

//Output
[1, 2, [[3, 4], [5, 6, [7]]], 8]

Untuk mengatasi masalah ini, kita dapat menggunakan logika loop + rekursi sederhana kita

func flattenedArray(array:[Any]) -> [Int] {
    var myArray = [Int]()
    for element in array {
        if let element = element as? Int {
            myArray.append(element)
        }
        if let element = element as? [Any] {
            let result = flattenedArray(array: element)
            for i in result {
                myArray.append(i)
            }

        }
    }
    return myArray
}

Jadi panggil fungsi ini dengan array yang diberikan

flattenedArray(array: array)

Hasilnya adalah:

[1, 2, 3, 4, 5, 6, 7, 8]

Fungsi ini akan membantu meratakan semua jenis larik, dengan mempertimbangkan kasus di Intsini

Output Taman Bermain: masukkan deskripsi gambar di sini

Rajan Maheshwari
sumber
2

Swift 4.2.0

Saya menulis ekstensi array sederhana di bawah ini. Anda dapat menggunakan untuk meratakan larik yang berisi larik atau elemen lain. tidak seperti metode join ().

public extension Array {
    public func flatten() -> [Element] {
        return Array.flatten(0, self)
    }

    public static func flatten<Element>(_ index: Int, _ toFlat: [Element]) -> [Element] {
        guard index < toFlat.count else { return [] }

        var flatten: [Element] = []

        if let itemArr = toFlat[index] as? [Element] {
            flatten = flatten + itemArr.flatten()
        } else {
            flatten.append(toFlat[index])
        }

        return flatten + Array.flatten(index + 1, toFlat)
    }
}

pemakaian:

let numbers: [Any] = [1, [2, "3"], 4, ["5", 6, 7], "8", [9, 10]]

numbers.flatten()
RahmiBozdag
sumber
1

Modifikasi jawaban @ RahmiBozdag, 1. Metode dalam ekstensi publik bersifat publik. 2. Metode ekstra dihapus, karena indeks awal akan selalu nol. 3. Saya tidak menemukan cara untuk meletakkan compactMap di dalam nil dan opsional karena di dalam metode T selalu [Ada?], Saran apa pun akan diterima.

 let array = [[[1, 2, 3], 4], 5, [6, [9], 10], 11, nil] as [Any?]

 public extension Array {

 func flatten<T>(_ index: Int = 0) -> [T] {
        guard index < self.count else { 
            return [] 
        }

        var flatten: [T] = []

        if let itemArr = self[index] as? [T] {
            flatten += itemArr.flatten()
        } else if let element = self[index] as? T {
            flatten.append(element)
        }
        return flatten + self.flatten(index + 1)
   }

}

let result: [Any] = array.flatten().compactMap { $0 }
print(result)
//[1, 2, 3, 4, 5, 6, 9, 10, 11]
Angsa
sumber
1

Apple Swift versi 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Target: x86_64-apple-darwin19.2.0

Screenshot

let optionalNumbers = [[1, 2, 3, nil], nil, [4], [5, 6, 7, 8, 9]]
print(optionalNumbers.compactMap { $0 }) // [[Optional(1), Optional(2), Optional(3), nil], [Optional(4)], [Optional(5), Optional(6), Optional(7), Optional(8), Optional(9)]]
print(optionalNumbers.compactMap { $0 }.reduce([], +).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(optionalNumbers.compactMap { $0 }.flatMap { $0 }.map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(optionalNumbers.compactMap { $0 }.joined()).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

let nonOptionalNumbers = [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.compactMap { $0 }) // [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.reduce([], +)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nonOptionalNumbers.flatMap { $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(nonOptionalNumbers.joined())) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
George
sumber
0

Implementasi lain yang lebih umum dari reduce,

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = reduce(numbers,[],+)

Ini menyelesaikan hal yang sama tetapi mungkin memberi lebih banyak wawasan tentang apa yang sedang terjadi reduce.

Dari dokumen Apple,

func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: (U, S.Generator.Element) -> U) -> U

Deskripsi

Kembalikan hasil penggabungan panggilan berulang dengan nilai akumulasi yang diinisialisasi ke awal dan setiap elemen urutan , secara bergantian.

Jim Hillhouse
sumber
Dengan kode Anda, saya mendapatkan:Use of unresolved identifier 'reduce'
Jason Moore
0

Anda dapat meratakan array bersarang menggunakan metode berikut:

var arrays = [1, 2, 3, 4, 5, [12, 22, 32], [[1, 2, 3], 1, 3, 4, [[[777, 888, 8999]]]]] as [Any]

func flatten(_ array: [Any]) -> [Any] {

    return array.reduce([Any]()) { result, current in
        switch current {
        case(let arrayOfAny as [Any]):
            return result + flatten(arrayOfAny)
        default:
            return result + [current]
        }
    }
}

let result = flatten(arrays)

print(result)

/// [1, 2, 3, 4, 5, 12, 22, 32, 1, 2, 3, 1, 3, 4, 777, 888, 8999]
Melvin John
sumber
0

Swift 5.1.0

public extension Array where Element: Collection {

    func flatten() -> [Element.Element] {
        return reduce([], +)
    }
}

Jika Anda juga menginginkannya untuk nilai Kamus:

public extension Dictionary.Values where Value : Collection {
    func flatten() -> [Value.Element]{
         return self.reduce([], +)
    }
}
Francisco Durdin Garcia
sumber
-1

matriks adalah [[myDTO]]?

Di swift 5 Anda bisa menggunakan this = Array (self.matrix! .Joined ())

dgalluccio.dll
sumber
-2
func convert(){
    let arr = [[1,2,3],[4],[5,6,7,8,9]]
    print("Old Arr = ",arr)
    var newArr = [Int]()
    for i in arr{
        for j in i{
            newArr.append(j)
        }
    }
    print("New Arr = ",newArr)
}

masukkan deskripsi gambar di sini

Rajesh Sharma
sumber