Achatar uma matriz de matrizes no Swift

142

Existe uma contrapartida em Swift para flattenScala, Xtend, Groovy, Ruby e companhia?

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

é claro que eu poderia usar reduzir para isso, mas que meio que é uma porcaria

var flattened = aofa.reduce(Int[]()){
    a,i in var b : Int[] = a
    b.extend(i)
    return b
}
Christian Dietrich
fonte
não é como usar o objeto add de uma matriz?
Pham Hoan
Ainda não examinei o Swift, mas em Haskell e F # é `concat` - então talvez pareça algo assim chamado? - Estou bastante positivo que este é lá em algum lugar (a maioria langs FP. Saber sobre mônadas e este é ligamento da Lista)
Carsten
sim, em haskell, na verdade é chamado concat.
Christian Dietrich
Você deve aceitar a resposta de andreschneider .
Rob

Respostas:

435

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

insira a descrição da imagem aqui

andreschneider
fonte
3
Apenas para afirmar isso de maneira mais geral, flatMapestá disponível a partir do Swift 1.2.
Mick MacCallum
3
qual é a diferença entre joined(formalmente conhecido como flatten) com flatMap? Será que, enquanto flatMapse junta, ele também pode mapear / transformar as coisas. mas aqui no exemplo que realmente não precisa, ou seja, voltamos$0
Mel
6
@Dschee flatMapvai quer achatar uma matriz 2D em uma matriz de 1D ou remove nilvalores, mas não ambos. Ele determina o que fazer com base se a matriz de primeiro nível Elementé uma matriz ou opcional - portanto, se você passar uma matriz 2D de opcionais (por exemplo [[Int?]]), ela escolherá achatar para 1D (por exemplo [Int?]) . Para achatar para 1-D e remover nils de segundo nível, você precisaria array.flatMap { $0 }.flatMap { $0 }. Em outras palavras, o nivelamento de dimensão é equivalente Array(array.joined())e o nivelamento de remoção nula é equivalente array.filter{ $0 != nil }.map{ $0! }.
Slipp D. Thompson
1
@Warpling flatMapainda é apropriado para o uso descrito na pergunta (achatando uma matriz 2D para 1D). compactMapé explicitamente para remover nilitens de uma sequência, como uma variante de flatMapuma vez.
21419 Jim Jimovov
1
@mohamadrezakoohkan que está correto. Como sua matriz é do tipo [[Any]], a flatMapsimplesmente a transforma em um tipo de [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). E se aplicássemos flatMapnovamente, agiríamos de acordo com um 'Qualquer? type, onde o compilador não sabe mais se é um valor simples ou uma matriz em si.
Andreschneider #
31

Em Swift biblioteca padrão não é joinedfunção implementada para todos os tipos em conformidade com Sequenceo protocolo (ou flattenem SequenceTypeantes Swift 3), que inclui Array:

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

Em certos casos, o uso de joined()pode ser benéfico, pois retorna uma coleção lenta em vez de uma nova matriz, mas sempre pode ser convertida em uma matriz quando passada para o Array()inicializador, como no exemplo acima.

Max Desiatov
fonte
@chrisco, você pode explicar como minha resposta está incorreta e quais são os critérios para "resposta correta mais simples"? Você também pode dizer como a exclusão de uma resposta pode afetar a pergunta de alguma forma?
Max Desiatov
Tente executar seu snippet primeiro - o que você acha que ele faz? O que isso realmente faz? Qual foi a pergunta original? Sua resposta está correta? Caso contrário, seria melhor excluí-lo para melhorar a clareza desta postagem. Fiz o mesmo com respostas próprias incorretas.
22415 Chris Hover, São
1
@chrisco, muito obrigado por suas sugestões, mas eu corro snippets antes de publicá-los em qualquer lugar. E minha resposta está correta, pois retorna exatamente os mesmos resultados que o OP solicitado e usa menos código para isso. Admito que minha resposta original estava retornando uma coleção lenta, em vez de uma matriz, embora não houvesse restrição sobre isso na pergunta. Ainda não acho que a exclusão de uma resposta correta melhore a qualidade da pergunta de forma alguma.
MaxDiatov #
Este foi o meu ponto - que ao testar / imprimir a saída, você recebe um array de arrays: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Seu argumento é válido, porém, de que você pode acessá-lo como uma matriz plana; portanto, parece que a CustomStringConvertableimplementação é enganosa. Seu snippet de código estava e ainda está faltando um teste.
Chris Conover
1
A partir do swift 3.0, flatten()foi renomeado parajoined()
Sr. Xcoder 23/03
16

Swift 4.x / 5.x

Apenas para adicionar um pouco mais de complexidade à matriz, se houver uma matriz que contenha uma matriz de matrizes, flatMapela realmente falhará.

Suponha que a matriz seja

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

O que flatMapou compactMapretorna é:

array.compactMap({$0})

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

Para resolver esse problema, podemos usar nossa lógica simples de loop + recursão

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
}

Então, chame essa função com a matriz fornecida

flattenedArray(array: array)

O resultado é:

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

Essa função ajudará a achatar qualquer tipo de matriz, considerando o caso Intaqui

Saída do campo de jogos: insira a descrição da imagem aqui

Rajan Maheshwari
fonte
2

Swift 4.2

Eu escrevi uma extensão de matriz simples abaixo. Você pode usar para achatar uma matriz que contém outra matriz ou elemento. diferente do 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()
RahmiBozdag
fonte
1

Outra implementação mais genérica de reduce,

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

Isso realiza a mesma coisa, mas pode fornecer mais informações sobre o que está acontecendo reduce.

Dos documentos da Apple,

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

Descrição

Retorne o resultado da chamada repetida de combinar com um valor acumulado inicializado para inicial e cada elemento da sequência , por sua vez.

Jim Hillhouse
fonte
Com seu código eu recebo:Use of unresolved identifier 'reduce'
Jason Moore
1

Resposta de @ RahmiBozdag modificada, 1. Os métodos em extensões públicas são públicos. 2. Método extra removido, pois o índice inicial será sempre zero. 3. Não encontrei uma maneira de incluir o compactMap por nulo e opcionais, porque o método T sempre é [Qualquer?], Todas as sugestões são bem-vindas.

 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]
Cisne
fonte
0

Você pode nivelar a matriz aninhada usando o seguinte 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]
Melvin John
fonte
0

Apple Swift versão 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Alvo: x86_64-apple-darwin19.2.0

Captura de tela

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

Swift 5.1

public extension Array where Element: Collection {

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

Caso você também deseje valores de Dicionário:

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

matriz é [[myDTO]]?

No swift 5, você pode usar isso = Array (self.matrix! .Joined ())

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

insira a descrição da imagem aqui

Rajesh Sharma
fonte