Reduzieren Sie ein Array von Arrays in Swift

141

Gibt es in Swift ein Gegenstück zu flattenScala, Xtend, Groovy, Ruby und 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] 

Natürlich könnte ich dafür Reduzieren verwenden, aber das ist irgendwie scheiße

var flattened = aofa.reduce(Int[]()){
    a,i in var b : Int[] = a
    b.extend(i)
    return b
}
Christian Dietrich
quelle
Ist es nicht so, als würde man ein Objekt eines Arrays hinzufügen?
Pham Hoan
Ich habe Swift selbst noch nicht untersucht, aber in Haskell und F # ist es "concat" - also vielleicht so etwas aussehen? - Ich bin ziemlich sicher , dass dies irgendwo da ist (die meisten FP langs. Wissen über Monaden und dies ist List's Bindung)
Carsten
ja in haskell heißt es eigentlich concat.
Christian Dietrich
Sie sollten die Antwort von andreschneider akzeptieren .
Rob

Antworten:

433

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

Geben Sie hier die Bildbeschreibung ein

andreschneider
quelle
3
Nur um dies allgemeiner auszudrücken, flatMapist ab Swift 1.2 verfügbar.
Mick MacCallum
3
Was ist der Unterschied zwischen joined(formal bekannt als flatten) mit flatMap? Ist es so, dass es während des flatMapBeitritts auch Dinge abbilden / transformieren kann? aber hier im Beispiel brauchen wir wirklich nicht, dh wir kehren zurück$0
Honey
6
@ Scheee flatMap wird entweder ein 2D - Array in ein 1D - Array abzuflachen oder Entfernen nilWerte, nicht beide. Es bestimmt, was zu tun ist, basierend darauf, ob das Array der ersten Ebene Elementein Array oder optional ist. Wenn Sie also ein 2D-Array mit Optionen übergeben (z. B. [[Int?]]) , wird es auf 1D (z[Int?] . B. ) reduziert . Um beide auf 1-D zu reduzieren und Nullen der 2. Ebene zu entfernen, müssten Sie dies tun array.flatMap { $0 }.flatMap { $0 }. Mit anderen Worten ist die Abflachung der Abmessungen gleich Array(array.joined())und die "Abflachung" ohne Entfernung gleich array.filter{ $0 != nil }.map{ $0! }.
Slipp D. Thompson
1
@Warpling flatMapist weiterhin für die in der Frage beschriebene Verwendung geeignet (Reduzieren eines 2D-Arrays auf 1D). compactMapdient explizit zum Entfernen von nilElementen aus einer Sequenz, wie es eine Variante von flatMapeinmal getan hat.
Jim Dovey
1
@mohamadrezakoohkan das ist richtig. Da Ihr Array vom Typ ist [[Any]], flatMapwandelt a es einfach in einen Typ von [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]) um. Und wenn wir uns flatMaperneut bewerben würden, würden wir auf ein `Any? Typ, bei dem der Compiler nicht mehr weiß, ob es sich um einen einfachen Wert oder ein Array selbst handelt.
Andreschneider
31

In Swift - Standardbibliothek ist es joinedFunktion für alle Arten implementiert konform SequenceProtokoll (oder flattenauf SequenceTypevor Swift 3), die umfasst Array:

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

In bestimmten Fällen joined()kann die Verwendung von vorteilhaft sein, da sie eine verzögerte Auflistung anstelle eines neuen Arrays zurückgibt, aber immer in ein Array konvertiert werden kann, wenn sie Array()wie im obigen Beispiel an den Initialisierer übergeben wird.

Max Desiatov
quelle
@chrisco Kannst du bitte näher erläutern, wie meine Antwort falsch ist und welche Kriterien für die "einfachste richtige Antwort" gelten? Können Sie bitte auch sagen, wie sich das Löschen einer Antwort auf die Frage auswirken kann?
Max Desiatov
Versuchen Sie zuerst, Ihr Snippet auszuführen - was macht es Ihrer Meinung nach? Was macht es eigentlich? Was war die ursprüngliche Frage? Ist Ihre Antwort richtig? Wenn nicht, ist es besser, es zu löschen, um die Klarheit dieses Beitrags zu verbessern. Ich habe dasselbe mit eigenen falschen Antworten getan.
Chris Conover
1
@chrisco Vielen Dank für Ihre Vorschläge, aber ich führe Snippets aus, bevor ich sie irgendwo poste. Und meine Antwort ist richtig, da sie genau die gleichen Ergebnisse wie das angeforderte OP zurückgibt und dafür weniger Code verwendet. Ich gebe zu, dass meine ursprüngliche Antwort darin bestand, eine faule Sammlung anstelle eines Arrays zurückzugeben, obwohl dies in der Frage nicht eingeschränkt war. Ich denke immer noch nicht, dass das Löschen einer korrekten Antwort die Qualität der Frage in irgendeiner Weise verbessert.
Max Desiatov
Dies war mein Punkt - beim Testen / Drucken der Ausgabe erhalten Sie ein Array von Arrays : FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Ihr Punkt ist jedoch gültig, da Sie wie ein flaches Array darauf zugreifen können. Es scheint also, dass die CustomStringConvertableImplementierung irreführend ist. Ihr Code-Snippet war und ist jedoch noch nicht getestet.
Chris Conover
1
Ab Swift 3.0 flatten()wurde umbenannt injoined()
Mr. Xcoder
16

Swift 4.x / 5.x.

Nur um dem Array etwas mehr Komplexität zu verleihen: Wenn es ein Array gibt, das ein Array von Arrays enthält, schlägt dies flatMaptatsächlich fehl.

Angenommen, das Array ist

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

Was flatMapodercompactMap kehrt zurück ist:

array.compactMap({$0})

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

Um dieses Problem zu lösen, können wir unsere einfache for-Schleifenlogik + Rekursion verwenden

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
}

Rufen Sie diese Funktion also mit dem angegebenen Array auf

flattenedArray(array: array)

Das Ergebnis ist:

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

Diese Funktion hilft dabei, jede Art von Array zu reduzieren, wenn man den Fall Inthier betrachtet

Spielplatzausgabe: Geben Sie hier die Bildbeschreibung ein

Rajan Maheshwari
quelle
2

Swift 4.2

Ich habe unten eine einfache Array-Erweiterung geschrieben. Sie können ein Array reduzieren, das ein anderes Array oder Element enthält. im Gegensatz zu join () Methode.

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

Verwendung:

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

numbers.flatten()
RahmiBozdag
quelle
1

Eine weitere allgemeinere Implementierung von reduce,

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

Dies bewirkt dasselbe, kann jedoch mehr Einblick in das geben, was in dem Geschehen vor sich geht reduce.

Aus Apples Dokumenten,

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

Beschreibung

Bringen Sie das Ergebnis der wiederholten Aufruf kombinieren mit einem akkumulierten Wert initialisiert anfänglichen und jedes Element der Sequenz , in der Reihe.

Jim Hillhouse
quelle
Mit Ihrem Code bekomme ich:Use of unresolved identifier 'reduce'
Jason Moore
1

Geänderte Antwort von @ RahmiBozdag: 1. Methoden in öffentlichen Erweiterungen sind öffentlich. 2. Zusätzliche Methode entfernt, da der Startindex immer Null ist. 3. Ich habe keine Möglichkeit gefunden, compactMap für Null und Optionen einzufügen, da in Methode T immer [Beliebig?] Ist. Vorschläge sind willkommen.

 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]
Schwan
quelle
0

Sie können verschachtelte Arrays mit der folgenden Methode reduzieren:

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
quelle
0

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

Bildschirmfoto

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
quelle
0

Swift 5.1

public extension Array where Element: Collection {

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

Falls Sie es auch für Wörterbuchwerte möchten:

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

Matrix ist myDTO?

In Swift 5 können Sie this = Array (self.matrix! .Joined ()) verwenden.

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

Geben Sie hier die Bildbeschreibung ein

Rajesh Sharma
quelle