Acoplar una matriz de matrices en Swift


144

¿Hay una contraparte en Swift to flattenen Scala, Xtend, Groovy, Ruby y compañía?

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

por supuesto que podría usar reducir para eso, pero eso apesta

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

¿No es como usar agregar objeto de una matriz?
Pham Hoan

Todavía no busqué en Swift, pero en Haskell y F # es `concat`, ¿entonces tal vez parezca algo así? - Estoy bastante seguro de que esto está allí en algún lugar (la mayoría de los FPs saben sobre mónadas y esto es el enlace de List)
Carsten

Sí, en Haskell en realidad se llama concat.
Christian Dietrich


Respuestas:


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

ingrese la descripción de la imagen aquí


3
Solo para decir esto de manera más general, flatMapestá disponible a partir de Swift 1.2.
Mick MacCallum

3
¿Cuál es la diferencia entre joined(formalmente conocido como flatten) con flatMap? Es que mientras se flatMapune, también puede mapear / transformar cosas. pero aquí en el ejemplo realmente no necesitamos, es decir, volvemos$0
Miel

66
@Dschee flatMapserá ya sea aplanar una matriz 2D en una matriz de 1D o quitan nilvalores, no ambos. Determina qué hacer en función de si la matriz del primer nivel Elementes una matriz u opcional, por lo que si le pasa una matriz 2D de opciones (por ejemplo [[Int?]]) , elegirá aplanarla a 1D (por ejemplo [Int?]) . Para aplanar a 1-D y eliminar nulos de segundo nivel, tendría que hacerlo array.flatMap { $0 }.flatMap { $0 }. En otras palabras, el achatamiento de dimensión es equivalente Array(array.joined())y el "aplanamiento" de eliminación nula es equivalente array.filter{ $0 != nil }.map{ $0! }.
Slipp D. Thompson

1
@Warpling flatMapsigue siendo apropiado para el uso descrito en la pregunta (alisar una matriz 2D a 1D). compactMapes explícitamente para eliminar nilelementos de una secuencia, como lo hizo una variante de flatMapuna vez.
Jim Dovey

1
@mohamadrezakoohkan eso es correcto. Como su matriz es de tipo [[Any]], flatMapsimplemente la transforma en un tipo de [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). Y si volviéramos a presentar flatMapuna solicitud , actuaríamos según un `¿Alguno? tipo, donde el compilador ya no sabe si es un valor simple o una matriz en sí misma.
andreschneider

31

En biblioteca estándar Swift hay joinedfunción implementada para todos los tipos conformes con Sequenceel protocolo (o flattenen SequenceTypeantes de Swift 3), que incluye Array:

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

En ciertos casos, el uso de joined()puede ser beneficioso ya que devuelve una colección diferida en lugar de una nueva matriz, pero siempre se puede convertir en una matriz cuando se pasa al Array()iniciador como en el ejemplo anterior.


@chrisco, ¿puede explicar cómo mi respuesta es incorrecta y cuál es el criterio para la "respuesta correcta más simple"? ¿Puedes decir también cómo eliminar una respuesta podría afectar la pregunta de alguna manera?
Max Desiatov el

Intente ejecutar su fragmento primero: ¿qué cree que hace? ¿Qué es lo que hace? ¿Cuál fue la pregunta original? ¿Es correcta su respuesta? Si no, sería mejor eliminarlo para mejorar la claridad de esta publicación. He hecho lo mismo con mis propias respuestas incorrectas.
Chris Conover

1
@chrisco muchas gracias por sus sugerencias, pero ejecuto fragmentos antes de publicarlos en cualquier lugar. Y mi respuesta es correcta, ya que devuelve exactamente los mismos resultados que OP solicitó y usa menos código para eso. Admito que mi respuesta original fue la devolución de la colección perezosa en lugar de una matriz, aunque no hubo restricciones en la pregunta. Todavía no creo que la eliminación de una respuesta correcta mejore la calidad de la pregunta de ninguna manera.
Max Desiatov

Este fue mi punto - que cuando se prueba / impresión de la salida, se obtiene una matriz de matrices: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Sin embargo, su punto es válido para poder acceder a él como una matriz plana, por lo que parece que la CustomStringConvertableimplementación es engañosa. Sin embargo, su fragmento de código era y aún falta una prueba.
Chris Conover

1
A partir de swift 3.0, flatten()se le cambió el nombre ajoined()
Mr. Xcoder el

16

Swift 4.x / 5.x

Solo para agregar un poco más de complejidad en la matriz, si hay una matriz que contiene una matriz de matrices, entonces flatMap realmente fallará.

Supongamos que la matriz es

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

Lo que flatMapo compactMapdevuelve es:

array.compactMap({$0})

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

Para resolver este problema, podemos usar nuestro simple for loop logic + recursion

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
}

Entonces llame a esta función con la matriz dada

flattenedArray(array: array)

El resultado es:

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

Esta función ayudará a aplanar cualquier tipo de matriz, considerando el caso de Intaquí

Salida del patio de recreo: ingrese la descripción de la imagen aquí




2

Swift 4.2

Escribí una extensión de matriz simple a continuación. Puede usar para aplanar una matriz que contiene otra matriz o elemento. a diferencia del método 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)
    }
}

uso:

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

numbers.flatten()

1

Otra implementación más genérica de reduce,

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

Esto logra lo mismo, pero puede dar más información sobre lo que está sucediendo reduce.

De los documentos de Apple,

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

Descripción

Devuelve el resultado de llamar repetidamente a combinar con un valor acumulado inicializado a inicial y cada elemento de secuencia , a su vez.


Con su código obtengo:Use of unresolved identifier 'reduce'
Jason Moore

1

Respuesta de @ RahmiBozdag modificada, 1. Los métodos en extensiones públicas son públicos. 2. Se eliminó el método adicional, ya que el índice de inicio siempre será cero. 3. No encontré una manera de poner compactMap dentro de nil y opcionales porque dentro del método T siempre es [Any?], Cualquier sugerencia es bienvenida.

 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

Puede aplanar una matriz anidada utilizando el siguiente método:

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 versión 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Objetivo: x86_64-apple-darwin19.2.0

Captura de pantalla

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

En caso de que también lo desee para los valores del Diccionario:

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


-1

matriz es [[myDTO]]?

En swift 5 puede usar 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)
}

ingrese la descripción de la imagen aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.