Aplatir un tableau de tableaux dans Swift


144

Y a-t-il une contrepartie dans Swift à flattenScala, Xtend, Groovy, Ruby and 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] 

Bien sûr, je pourrais utiliser réduire pour ça mais ça craint un peu

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

n'est-ce pas comme utiliser un objet add d'un tableau?
Pham Hoan

Je n'ai pas encore regardé dans Swift lui-même mais dans Haskell et F # c'est `concat` - alors peut-être ressembler à quelque chose nommé comme ça? - Je suis plutôt sûr que c'est là quelque part (la plupart des langages FP connaissent les monades et c'est le lien de List)
Carsten

oui dans haskell il s'appelle en fait concat.
Christian Dietrich

Vous devriez accepter la réponse d'Andreschneider .
Rob

Réponses:


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

entrez la description de l'image ici


3
Pour le dire plus généralement, flatMapest disponible à partir de Swift 1.2.
Mick MacCallum

3
Quelle est la différence entre joined(officiellement connu sous le nom de flatten) avec flatMap? Est-ce que pendant les flatMapjointures, il peut également mapper / transformer des choses. mais ici, dans l'exemple, nous n'avons vraiment pas besoin, c'est-à-dire que nous revenons$0
Honey

6
@Dschee flatMapse soit aplatir un tableau 2D dans un tableau 1D ou remove nilvaleurs, mais pas les deux. Il détermine ce qu'il faut faire en fonction si le tableau de premier niveau Elementest un tableau ou facultatif - donc si vous lui passez un tableau 2D d'options (par exemple [[Int?]]), il choisira de l'aplatir en 1D (par exemple [Int?]) . Pour à la fois aplatir en 1-D et supprimer les nils de 2e niveau, vous devez le faire array.flatMap { $0 }.flatMap { $0 }. En d'autres termes, l'aplatissement des dimensions équivaut à Array(array.joined())et l '«aplatissement» de suppression de zéro équivaut à array.filter{ $0 != nil }.map{ $0! }.
Slipp D.Thompson

1
@Warpling flatMapest toujours approprié pour l'utilisation décrite dans la question (aplatissement d'un tableau 2D en 1D). compactMapest explicitement pour supprimer des niléléments d'une séquence, comme une variante de l' flatMapa fait une fois.
Jim Dovey

1
@mohamadrezakoohkan c'est correct. Puisque votre tableau est de type [[Any]], a flatMaple transforme simplement en un type de [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). Et si nous postulions à flatMapnouveau, nous agirions sur un «Any? type, où le compilateur ne sait plus s'il s'agit d'une valeur simple ou d'un tableau lui-même.
andreschneider

31

Dans la bibliothèque standard Swift il est joinedfonction mise en oeuvre pour tous les types conformes au Sequenceprotocole (ou flattensur SequenceTypeavant Swift 3), qui comprend Array:

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

Dans certains cas, l'utilisation de joined()peut être bénéfique car elle renvoie une collection différée au lieu d'un nouveau tableau, mais peut toujours être convertie en tableau lorsqu'elle est transmise à l' Array()initialiser comme dans l'exemple ci-dessus.


@chrisco pouvez-vous s'il vous plaît expliquer en quoi ma réponse est incorrecte et quels sont les critères de «réponse correcte la plus simple»? Pouvez-vous également dire comment la suppression d'une réponse pourrait avoir un impact sur la question?
Max Desiatov

Essayez d'abord d'exécuter votre extrait - que pensez-vous qu'il fait? Que fait-il réellement? Quelle était la question initiale? Votre réponse est-elle correcte? Sinon, il serait préférable de le supprimer pour améliorer la clarté de ce message. J'ai fait de même avec mes propres réponses incorrectes.
Chris Conover

1
@chrisco merci beaucoup pour vos suggestions, mais je lance des extraits avant de les publier n'importe où. Et ma réponse est correcte car elle renvoie exactement les mêmes résultats que OP demandé et en utilisant moins de code pour cela. J'admets que ma réponse originale renvoyait une collection paresseuse au lieu d'un tableau, bien qu'il n'y ait aucune restriction à ce sujet dans la question. Je ne pense toujours pas que la suppression d'une réponse correcte améliore en aucune façon la qualité de la question.
Max Desiatov

Ce fut mon point - que lors du test / impression de la sortie, vous obtenez un tableau de tableaux: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Votre argument est valable cependant que vous pouvez y accéder comme un tableau plat, il semblerait donc que la CustomStringConvertablemise en œuvre soit trompeuse. Cependant, votre extrait de code manquait et il manque toujours un test.
Chris Conover

1
À partir de swift 3.0, flatten()a été renommé enjoined()
Mr. Xcoder

16

Swift 4.x / 5.x

Juste pour ajouter un peu plus de complexité au tableau, s'il existe un tableau contenant un tableau de tableaux, alors flatMap échouera en fait.

Supposons que le tableau soit

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

Ce que flatMapou compactMapretourne est:

array.compactMap({$0})

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

Afin de résoudre ce problème, nous pouvons utiliser notre logique de boucle for simple + récursivité

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
}

Appelez donc cette fonction avec le tableau donné

flattenedArray(array: array)

Le résultat est:

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

Cette fonction aidera à aplatir tout type de tableau, en considérant le cas de Int ici

Sortie de terrain de jeu: entrez la description de l'image ici




2

Swift 4.2

J'ai écrit une simple extension de tableau ci-dessous. Vous pouvez utiliser pour aplatir un tableau qui contient un autre tableau ou élément. contrairement à la méthode 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)
    }
}

usage:

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

numbers.flatten()

1

Une autre implémentation plus générique de reduce,

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

Cela accomplit la même chose mais peut donner plus d'informations sur ce qui se passe dans reduce .

À partir de la documentation d'Apple,

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

La description

Renvoie le résultat d'appels répétés combiner avec une valeur accumulée initialisée à initial et chaque élément de séquence , à son tour.


Avec votre code, j'obtiens:Use of unresolved identifier 'reduce'
Jason Moore le

1

Réponse modifiée de @ RahmiBozdag, 1. Les méthodes dans les extensions publiques sont publiques. 2. Suppression de la méthode supplémentaire, car l'index de départ sera toujours zéro. 3. Je n'ai pas trouvé de moyen de mettre compactMap à l'intérieur pour nil et optionnel car à l'intérieur de la méthode T est toujours [Any?], Toutes les suggestions sont les bienvenues.

 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

Vous pouvez aplatir un tableau imbriqué à l'aide de la méthode suivante:

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 version 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Cible: x86_64-apple-darwin19.2.0

Capture d'écran

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([], +)
    }
}

Si vous le souhaitez également pour les valeurs du dictionnaire:

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


-1

matrice est [[myDTO]]?

Dans swift 5, vous pouvez utiliser 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)
}

entrez la description de l'image ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.