How can I flatten an array swiftily in Swift?

2019-02-21 08:39发布

问题:

I want to turn this:

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

into this:

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

very gracefully.

The most straightforward way, of course, is

var y = [Int]()
x.forEach { y.appendContentsOf($0) }

But that makes the resulting array mutable, which is unnecessary. I don't like this way.

I tried using reduce:

let y = x.reduce([Int]()) { (array, ints) -> [Int] in
    array.appendContentsOf(ints)
    return array
}

But the compiler complains that array is immutable, so I can't call the mutating method appendContentsOf.

Hence, I added some stuff:

let y = x.reduce([Int]()) { (array, ints) -> [Int] in
    var newArr = array
    newArr.appendContentsOf(ints)
    return newArr
}

This is just plain bad. I have an instinct that this is not swifty.

How can I flatten an array more swiftily than the above methods? A one-liner would be good.

回答1:

There's a built-in function for this called joined:

[[1, 2, 3], [4, 5, 6], [7, 8, 9]].joined()

(Note that this doesn't actually return another Array, it returns a FlattenBidirectionalCollection, but that usually doesn't matter because it's still a sequence that you can use with for loops and whatnot. If you really care, you can use Array(arrayOfArrays.joined()).)


The flatMap function can also help you out. Its signature is, roughly,

flatMap<S: SequenceType>(fn: (Generator.Element) -> S) -> [S.Generator.Element]

This means that you can pass a fn which for any element returns a sequence, and it'll combine/concatenate those sequences.

Since your array's elements are themselves sequences, you can use a function which just returns the element itself ({ x in return x } or equivalently just {$0}):

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


回答2:

With Swift 3, according to your needs and tastes, you may choose one of the three following ways to flatten an array.


1. Flatten an array with Sequence's flatMap(_:) method

Swift provides a flatMap(_:) method for all types that conform to Sequence protocol (including Array). flatMap(_:) has the following declaration:

func flatMap<SegmentOfResult : Sequence>(_ transform: (Self.Iterator.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Iterator.Element]

Returns an array containing the concatenated results of calling the given transformation with each element of this sequence.

The following Playground code shows how to flatten an Array of type [[Int]] to type [Int] using flatMap(_:) method:

let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattenArray = array.flatMap({ (element: [Int]) -> [Int] in
    return element
})
print(flattenArray) // prints [1, 2, 3, 4, 5, 6, 7, 8, 9]

2. Flatten an array with Sequence's joined() method and Array's init(_:) initializer

Swift also provides a joined() method for all types that conform to Sequence protocol (including Array). joined() has the following declaration:

func joined() -> FlattenSequence<Self>

Returns the elements of this sequence of sequences, concatenated.

Besides, Swift Array has a init(_:) initializer. init(_:) has the following declaration:

init<S : Sequence where S.Iterator.Element == Element>(_ s: S)

Creates an array containing the elements of a sequence.

Therefore, the following Playground code shows how to flatten an Array of type [[Int]] to type [Int] using joined() method and init(_:) initializer:

let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattenCollection = array.joined() // type: FlattenBidirectionalCollection<[Array<Int>]>
let flattenArray = Array(flattenCollection)
print(flattenArray) // prints [1, 2, 3, 4, 5, 6, 7, 8, 9]

3. Flatten an array with Array's reduce(_:_:) method

Swift Array has a reduce(_:_:) method. reduce(_:_:) has the following declaration:

func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

Returns the result of calling the given combining closure with each element of this sequence and an accumulating value.

The following Playground code shows how to flatten an Array of type [[Int]] to type [Int] using reduce(_:_:) method:

let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattenArray = array.reduce([], { (result: [Int], element: [Int]) -> [Int] in
    return result + element
})
print(flattenArray) // prints [1, 2, 3, 4, 5, 6, 7, 8, 9]


回答3:

Flatten function in Array :

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let y = Array(x.flatten())
print(y)

flatMap function in Array :

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let y = x.flatMap({$0})
print(y)

Your Output :

For more : Array in Swift