Spłaszcz tablicę tablic w języku Swift

143

Czy istnieje odpowiednik w Swift flattenw Scali, Xtend, Groovy, Ruby i 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] 

oczywiście mógłbym użyć do tego redukcji, ale to trochę jest do bani

var flattened = aofa.reduce(Int[]()){
    a,i in var b : Int[] = a
    b.extend(i)
    return b
}
Christian Dietrich
źródło
czy to nie jest jak użycie obiektu add z tablicy?
Pham Hoan
Nie zajrzałem jeszcze do samego Swifta, ale w Haskell i F # jest to `concat` - więc może zobacz coś takiego? - Jestem raczej pewien, że to gdzieś tam jest (większość języków FP. Wiem o monadach i to jest wiązanie listy)
Carsten
tak w haskell faktycznie nazywa się to concat.
Christian Dietrich
Powinieneś zaakceptować odpowiedź andreschneidera .
Rob

Odpowiedzi:

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

wprowadź opis obrazu tutaj

andreschneider
źródło
3
Mówiąc bardziej ogólnie, flatMapjest dostępny od wersji Swift 1.2.
Mick MacCallum
3
Jaka jest różnica między joined(formalnie nazywana flatten) with flatMap? Czy to, że podczas flatMapłączenia może również mapować / przekształcać rzeczy. ale tutaj w przykładzie naprawdę nie potrzebujemy, czyli wracamy$0
Kochanie
6
@Dschee flatMapbędzie albo spłaszczenie tablicy 2D w tablicy 1D lub usuni nilwartości nie jednocześnie. Określa, co zrobić na podstawie tego, czy tablica pierwszego poziomu Elementjest tablicą, czy opcjonalna - więc jeśli przekażesz jej tablicę opcji 2D (np. [[Int?]]) , Zdecyduje się spłaszczyć ją do 1D (np[Int?] . ) . Aby zarówno spłaszczyć do 1-D, jak i usunąć nile drugiego poziomu, musisz zrobić array.flatMap { $0 }.flatMap { $0 }. Innymi słowy, spłaszczenie wymiaru jest równoważne, Array(array.joined())a „spłaszczenie” z usunięciem zera jest równoważne array.filter{ $0 != nil }.map{ $0! }.
Slipp D. Thompson
1
@Warpling flatMapjest nadal odpowiedni do zastosowania opisanego w pytaniu (spłaszczanie tablicy 2D do 1D). compactMapsłuży wyraźnie do usuwania nilelementów z sekwencji, tak jak flatMapkiedyś.
Jim Dovey
1
@mohamadrezakoohkan, to jest poprawne. Ponieważ twoja tablica jest typu [[Any]], a flatMappo prostu przekształca ją na typ [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). A gdybyśmy flatMapponownie złożyli wniosek , działalibyśmy zgodnie z zasadą „Any? typ, gdzie kompilator nie wie już, czy jest to prosta wartość, czy sama tablica.
andreschneider
31

Swift standardowej bibliotece jest joinedfunkcją realizowane dla wszystkich typów zgodnych z Sequenceprotokołem (albo flattenna SequenceTypewcześniej Swift 3), który obejmuje Array:

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

W niektórych przypadkach użycie of joined()może być korzystne, ponieważ zwraca leniwą kolekcję zamiast nowej tablicy, ale zawsze można je przekonwertować na tablicę po przekazaniu do Array()inicjatora, jak w powyższym przykładzie.

Max Desiatov
źródło
@chrisco czy możesz wyjaśnić, dlaczego moja odpowiedź jest nieprawidłowa i jakie są kryteria „najprostszej poprawnej odpowiedzi”? Czy możesz również powiedzieć, jak usunięcie odpowiedzi może wpłynąć w jakikolwiek sposób na pytanie?
Max Desiatov
Spróbuj najpierw uruchomić swój fragment - jak myślisz, co on robi? Co to właściwie robi? Jakie było pierwotne pytanie? Czy twoja odpowiedź jest prawidłowa? Jeśli nie, lepiej byłoby go usunąć, aby poprawić czytelność tego postu. Zrobiłem to samo z własnymi błędnymi odpowiedziami.
Chris Conover
1
@chrisco bardzo dziękuję za sugestie, ale przed umieszczeniem ich w dowolnym miejscu uruchamiam fragmenty. Moja odpowiedź jest poprawna, ponieważ zwraca dokładnie takie same wyniki, jak żądane przez OP i używa do tego mniej kodu. Przyznaję, że moja pierwotna odpowiedź zwracała leniwą kolekcję zamiast tablicy, chociaż nie było co do tego ograniczenia w pytaniu. Nadal nie sądzę, aby usunięcie poprawnej odpowiedzi w jakikolwiek sposób poprawiło jakość pytania.
Max Desiatov
To był mój punkt widzenia - że podczas testowania / drukowania wyjście, masz tablicę tablic: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Twój punkt widzenia jest jednak słuszny, że możesz uzyskać do niego dostęp jak do płaskiej tablicy, więc wydawałoby się, że CustomStringConvertableimplementacja wprowadza w błąd. Twój fragment kodu był i nadal nie ma testu.
Chris Conover,
1
Od wersji swift 3.0 flatten()została zmieniona najoined()
Mr. Xcoder
16

Swift 4.x / 5.x

Aby dodać trochę więcej złożoności do tablicy, jeśli istnieje tablica zawierająca tablicę tablic, to flatMapfaktycznie się nie powiedzie.

Załóżmy, że tablicą jest

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

Co flatMaplub compactMapzwraca:

array.compactMap({$0})

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

Aby rozwiązać ten problem, możemy użyć naszej prostej logiki pętli for + rekurencji

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
}

Wywołaj więc tę funkcję z podaną tablicą

flattenedArray(array: array)

Wynik to:

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

Ta funkcja pomoże spłaszczyć każdy rodzaj tablicy, biorąc pod uwagę przypadek Inttutaj

Wyjście na plac zabaw: wprowadź opis obrazu tutaj

Rajan Maheshwari
źródło
2

Swift 4.2

Poniżej napisałem proste rozszerzenie tablicy. Możesz użyć do spłaszczenia tablicy, która zawiera inną tablicę lub element. w przeciwieństwie do linked () metody.

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

stosowanie:

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

numbers.flatten()
RahmiBozdag
źródło
1

Innym bardziej ogólne wdrażanie reduce,

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

Osiąga to to samo, ale może dać lepszy wgląd w to, co się dzieje reduce.

Z dokumentów Apple,

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

Opis

Zwróć wynik wielokrotnego wywoływania kombajnu ze skumulowaną wartością zainicjowaną do wartości początkowej i każdego elementu sekwencji po kolei.

Jim Hillhouse
źródło
Z twoim kodem otrzymuję:Use of unresolved identifier 'reduce'
Jason Moore
1

Zmodyfikowana odpowiedź @ RahmiBozdag, 1. Metody w rozszerzeniach publicznych są publiczne. 2. Usunięto dodatkową metodę, ponieważ indeks początkowy będzie zawsze wynosił zero. 3. Nie znalazłem sposobu na umieszczenie compactMap wewnątrz dla nil i opcji, ponieważ wewnątrz metody T zawsze jest [Any?], Wszelkie sugestie są mile widziane.

 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]
Łabędź
źródło
0

Możesz spłaszczyć zagnieżdżoną tablicę za pomocą następującej metody:

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
źródło
0

Apple Swift w wersji 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Cel: x86_64-apple-darwin19.2.0

Zrzut ekranu

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]
Jerzy
źródło
0

Swift 5.1

public extension Array where Element: Collection {

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

W przypadku, gdy chcesz go również dla wartości słownika:

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

macierz to [[myDTO]]?

W Swift 5 możesz użyć this = Array (self.matrix! .Joined ())

dgalluccio
źródło
-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)
}

wprowadź opis obrazu tutaj

Rajesh Sharma
źródło