How can I interleave two arrays?

If I have two arrays e.g

let one = [1,3,5]
let two = [2,4,6]

I would like to merge/interleave the arrays in the following pattern [one[0], two[0], one[1], two[1] etc....]

//prints [1,2,3,4,5,6]
let comibned = mergeFunction(one, two)
print(combined)

What would be a good way to implement the combining function?

func mergeFunction(one: [T], _ two: [T]) -> [T] {
    var mergedArray = [T]()
    //What goes here
    return mergedArray
}

If both arrays have the same length then this is a possible solution:

let one = [1,3,5]
let two = [2,4,6]

let merged = zip(one, two).flatMap { [$0, $1] }

print(merged) // [1, 2, 3, 4, 5, 6]

Here zip() enumerates the arrays in parallel and returns a sequence of pairs (2-element tuples) with one element from each array. flatMap() creates a 2-element array from each pair and concatenates the result.

If the arrays can have different length then you append the extra elements of the longer array to the result:

func mergeFunction<T>(one: [T], _ two: [T]) -> [T] {
    let commonLength = min(one.count, two.count)
    return zip(one, two).flatMap { [$0, $1] } 
           + one.suffixFrom(commonLength)
           + two.suffixFrom(commonLength)
}

Update for Swift 3:

func mergeFunction<T>(_ one: [T], _ two: [T]) -> [T] {
    let commonLength = min(one.count, two.count)
    return zip(one, two).flatMap { [$0, $1] } 
           + one.suffix(from: commonLength)
           + two.suffix(from: commonLength)
}

If you're just looking to interleave two arrays, you could just do something like:

let maxIndex = max(one.count, two.count)
var mergedArray = Array<T>()
for index in 0..<maxIndex {
    if index < one.count { mergedArray.append(one[index]) }
    if index < two.count { mergedArray.append(two[index]) }
}

return mergedArray

With Swift 5, you can use one of the following Playground sample codes in order to solve your problem.


#1. Using zip(_:_:) function and Collection's flatMap(_:) method

let one = [1, 3, 5, 7]
let two = [2, 4, 6]

let array = zip(one, two).flatMap({ [$0, $1] })
print(array) // print: [1, 2, 3, 4, 5, 6]

Apple states:

If the two sequences passed to zip(_:_:) are different lengths, the resulting sequence is the same length as the shorter sequence.


#2. Using an object that conforms to Sequence and IteratorProtocol protocols

struct InterleavedSequence<T>: Sequence, IteratorProtocol {

    private let firstArray: [T]
    private let secondArray: [T]
    private let thresholdIndex: Int
    private var index = 0
    private var toggle = false

    init(firstArray: [T], secondArray: [T]) {
        self.firstArray = firstArray
        self.secondArray = secondArray
        self.thresholdIndex = Swift.min(firstArray.endIndex, secondArray.endIndex)
    }

    mutating func next() -> T? {
        guard index < thresholdIndex else { return nil }
        defer {
            if toggle {
                index += 1
            }
            toggle.toggle()
        }
        return !toggle ? firstArray[index] : secondArray[index]
    }

}

let one = [1, 3, 5, 7]
let two = [2, 4, 6]

let sequence = InterleavedSequence(firstArray: one, secondArray: two)
let array = Array(sequence)
print(array) // print: [1, 2, 3, 4, 5, 6]