Appiattisci una matrice di array in Swift


144

Esiste una controparte in Swift a flattenScala, Xtend, Groovy, Ruby e 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] 

ovviamente potrei usare la riduzione per quello, ma quel genere di cose fa schifo

var flattened = aofa.reduce(Int[]()){
    a,i in var b : Int[] = a
    b.extend(i)
    return b
}

non è come usare aggiungi oggetto di un array?
Pham Hoan,

Non ho ancora esaminato Swift stesso, ma in Haskell e F # è `concat` - quindi forse assomigli a qualcosa chiamato così? - Sono piuttosto sicuro che ci sia da qualche parte (la maggior parte dei lang di FP. Conoscono le monadi e questo è il legame di List)
Carsten,

sì in haskell in realtà si chiama concat.
Christian Dietrich,

Dovresti accettare la risposta di andreschneider .
Rob,

Risposte:


436

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

inserisci qui la descrizione dell'immagine


3
Giusto per affermarlo più in generale, flatMapè disponibile da Swift 1.2.
Mick MacCallum,

3
Qual è la differenza tra joined(formalmente noto come flatten) con flatMap? È possibile che mentre si flatMapunisce, può anche mappare / trasformare le cose. ma qui nell'esempio non abbiamo davvero bisogno cioè torniamo$0
Honey

6
@Dschee flatMapsi sia appiattire una matrice 2D in una matrice 1D o rimuovere nilvalori, non entrambi. Determina su cosa fare se l'array di 1 ° livello Elementè un array o opzionale, quindi se gli passi un array 2D di optional (ad es. [[Int?]]) Sceglierà di appiattirlo a 1D (ad es[Int?] . ) . Per appiattire entrambi in 1-D e rimuovere zero di 2 ° livello, dovresti farlo array.flatMap { $0 }.flatMap { $0 }. In altre parole, l'appiattimento dimensionale è equivalente a Array(array.joined())e l'appiattimento zero-rimozione è equivalente array.filter{ $0 != nil }.map{ $0! }.
Slipp D. Thompson,

1
@Warpling flatMapè ancora appropriato per l'uso descritto nella domanda (appiattimento di un array 2D a 1D). compactMapserve esplicitamente a rimuovere nilelementi da una sequenza, come una variante di flatMapuna volta.
Jim Dovey,

1
@mohamadrezakoohkan è corretto. Poiché il tuo array è di tipo [[Any]], a flatMaplo trasforma semplicemente in un tipo di [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). E se dovessimo applicare flatMapnuovamente, agiremmo su un `Any? tipo, in cui il compilatore non sa più se si tratta di un valore semplice o di un array stesso.
andreschneider,

31

Nella libreria standard Swift c'è joinedfunzione implementata per tutti i tipi conformi al Sequenceprotocollo (o flattensulla SequenceTypeprima Swift 3), che comprende Array:

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

In alcuni casi l'uso di joined()può essere utile in quanto restituisce una raccolta pigra anziché una nuova matrice, ma può sempre essere convertita in una matrice quando passata Array()all'inizializzatore come nell'esempio sopra.


@chrisco puoi per favore approfondire come la mia risposta è errata e quali sono i criteri per la "risposta corretta più semplice"? Potete per favore anche dire in che modo l'eliminazione di una risposta potrebbe influire in qualche modo sulla domanda?
Max Desiatov,

Prova a eseguire prima lo snippet: cosa pensi che faccia? Cosa fa effettivamente? Qual era la domanda originale? La tua risposta è corretta? In caso contrario, sarebbe meglio eliminarlo per migliorare la chiarezza di questo post. Ho fatto lo stesso con le mie risposte errate.
Chris Conover,

1
@chrisco ti ringrazio molto per i tuoi suggerimenti, ma eseguo frammenti prima di pubblicarli ovunque. E la mia risposta è corretta in quanto restituisce esattamente gli stessi risultati di OP richiesti e utilizzando meno codice per quello. Devo ammettere che la mia risposta originale stava restituendo una raccolta pigra anziché una matrice, sebbene non vi fosse alcuna limitazione in merito alla domanda. Continuo a non pensare che la cancellazione di una risposta corretta migliori in alcun modo la qualità della domanda.
Max Desiatov,

Questo è stato il mio punto - che durante il test / stampa l'uscita, si ottiene un array di array: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Il tuo punto è valido anche se puoi accedervi come un array piatto, quindi sembrerebbe che l' CustomStringConvertableimplementazione sia fuorviante. Lo snippet di codice era e manca ancora un test.
Chris Conover,

1
A partire da Swift 3.0, flatten()è stato rinominato injoined()
Mr. Xcoder il

16

Swift 4.x / 5.x

Solo per aggiungere un po 'più di complessità all'array, se esiste un array che contiene un array di array, flatMapin realtà fallirà.

Supponiamo che l'array sia

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

Cosa flatMapo compactMapritorna è:

array.compactMap({$0})

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

Per risolvere questo problema, possiamo usare la nostra logica semplice per loop + ricorsione

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
}

Quindi chiama questa funzione con l'array specificato

flattenedArray(array: array)

Il risultato è:

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

Questa funzione aiuterà ad appiattire qualsiasi tipo di array, considerando il caso Intqui

Uscita per parco giochi: inserisci qui la descrizione dell'immagine




2

Rapido 4.2

Di seguito ho scritto una semplice estensione di array. È possibile utilizzare per appiattire un array che contiene un altro array o elemento. a differenza del metodo united ().

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

utilizzo:

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

numbers.flatten()

1

Un'altra implementazione più generica di reduce,

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

Ciò realizza la stessa cosa ma può fornire maggiori informazioni su ciò che sta accadendo reduce.

Dai documenti di Apple,

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

Descrizione

Restituisce il risultato della ripetizione della chiamata combinata con un valore accumulato inizializzato su iniziale e ciascun elemento della sequenza , a sua volta.


Con il tuo codice ottengo:Use of unresolved identifier 'reduce'
Jason Moore il

1

Modificata la risposta di RahmiBozdag, 1. I metodi nelle estensioni pubbliche sono pubblici. 2. Rimosso il metodo aggiuntivo, poiché l'indice iniziale sarà sempre zero. 3. Non ho trovato il modo di inserire compactMap all'interno di zero e di opzioni perché all'interno del metodo T è sempre [Qualsiasi?], Vengono accolti eventuali suggerimenti.

 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]

0

È possibile appiattire l'array nidificato utilizzando il seguente metodo:

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]

0

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

Immagine dello schermo

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]

0

Swift 5.1

public extension Array where Element: Collection {

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

Nel caso lo desideri anche per i valori del Dizionario:

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


-1

matrice è [[myDTO]]?

In swift 5 puoi usare this = Array (self.matrix! .Joined ())


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

inserisci qui la descrizione dell'immagine

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.