Flatten an Array of Arrays in Swift

Swift

Swift Problem Overview


Is there a counterpart in Swift to flatten in Scala, 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] 

of course i could use reduce for that but that kinda sucks

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

Swift Solutions


Solution 1 - Swift

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

enter image description here

Solution 2 - Swift

In Swift standard library there is joined function implemented for all types conforming to Sequence protocol (or flatten on SequenceType before Swift 3), which includes Array:

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

In certain cases use of joined() can be beneficial as it returns a lazy collection instead of a new array, but can always be converted to an array when passed to Array() initialiser like in the example above.

Solution 3 - Swift

Swift 4.x/5.x

Just to add a bit more complexity in the array, if there is an array that contains array of arrays, then flatMap will actually fail.

Suppose the array is

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

What flatMap or compactMap returns is:

array.compactMap({$0})

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

In order to solve this problem, we can use our 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
}

So call this function with the given array

flattenedArray(array: array)

The Result is:

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

This function will help to flatten any kind of array, considering the case of Int here

Playground Output: enter image description here

Solution 4 - Swift

Swift 4.x

This usage of flatMap isn't deprecated and it's make for this. https://developer.apple.com/documentation/swift/sequence/2905332-flatmap

var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatMap { $0 } //[1,2,3,4,5,6,7,8,9] 

Solution 5 - Swift

Edit: Use joined() instead:

https://developer.apple.com/documentation/swift/sequence/2431985-joined

Original reply:

let numbers = [[1, 2, 3], [4, 5, 6]]
let flattenNumbers = numbers.reduce([], combine: +)

Solution 6 - Swift

Swift 4.2

I wrote a simple array extension below. You can use to flatten an array that contains another array or element. unlike joined() method.

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

Solution 7 - Swift

Swift 5.1

public extension Array where Element: Collection {

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

In case you also want it for Dictionary values:

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

Solution 8 - Swift

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

Screenshot

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]

Solution 9 - Swift

Solution 10 - Swift

You can flatten nested array using the following method:

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]

Solution 11 - Swift

Modified @RahmiBozdag's answer,

  1. Methods in public extensions are public.

  2. Removed extra method, as start index will be always zero.

  3. I did not find a way to put compactMap inside for nil and optionals because inside method T is always [Any?], any suggestions are welcomed.

    let array = [[[1, 2, 3], 4], 5, [6, [9], 10], 11, nil] as [Any?]

    public extension Array {

    func flatten(_ 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]

Solution 12 - Swift

Another more generic implementation of reduce,

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

This accomplishes the same thing but may give more insight into what is going on in reduce.

From Apple's docs,

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

Description

Return the result of repeatedly calling combine with an accumulated value initialized to initial and each element of sequence, in turn.

Solution 13 - Swift

matrix is [[myDTO]]?

In swift 5 you can use this = Array(self.matrix!.joined())

Solution 14 - Swift

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

enter image description here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChristian DietrichView Question on Stackoverflow
Solution 1 - SwiftandreschneiderView Answer on Stackoverflow
Solution 2 - SwiftMax DesiatovView Answer on Stackoverflow
Solution 3 - SwiftRajan MaheshwariView Answer on Stackoverflow
Solution 4 - SwiftNikLoyView Answer on Stackoverflow
Solution 5 - SwiftRudolf AdamkovičView Answer on Stackoverflow
Solution 6 - SwiftRahmiBozdagView Answer on Stackoverflow
Solution 7 - SwiftFrancisco Durdin GarciaView Answer on Stackoverflow
Solution 8 - SwiftGeorgeView Answer on Stackoverflow
Solution 9 - SwifthojinView Answer on Stackoverflow
Solution 10 - SwiftMelvin JohnView Answer on Stackoverflow
Solution 11 - SwiftSwanView Answer on Stackoverflow
Solution 12 - SwiftJim HillhouseView Answer on Stackoverflow
Solution 13 - SwiftdgalluccioView Answer on Stackoverflow
Solution 14 - SwiftRajesh SharmaView Answer on Stackoverflow