Technically, this is not guaranteed to preserve order, but it does.

Dictionary(grouping: numbers) { $0.isMultiple(of: 3) }

https://github.com/apple/swift/blob/master/stdlib/public/core/NativeDictionary.swift

Answer from user652038 on Stack Overflow
🌐
Hacking with Swift
hackingwithswift.com › example-code › language › how-to-split-an-array-into-chunks
How to split an array into chunks - free Swift example code and tips
May 28, 2019 - If you have an array of elements and you want to split them into chunks of a size you specify, here’s a useful extension you can add to your code: extension Array { func chunked(into size: Int) -> [[Element]] { return stride(from: 0, to: count, by: size).map { Array(self[$0 ..< Swift.min($0 + size, count)]) } } }
🌐
Swift Forums
forums.swift.org › evolution › discussion
Proposal: Filter split extension on Sequence to return tuple of sequences that meet criteria and that do not - Discussion - Swift Forums
June 8, 2016 - Many times, I came across a scenario where I had to filter an array with a condition and filter the same array with opposite of that condition. For example: let values = [2, 4, 3, 5, 6, 9] let divisibleByTwo = values…
🌐
Swift by Sundell
swiftbysundell.com › articles › slicing-swift-collections
Slicing Swift collections | Swift by Sundell
Let’s take a look at an example of doing just that, in which we’re using another “flavor” of the standard library’s prefix API (along with its suffix counterpart) to split a shipment of packages up into two separate shipments based on indexes. Since we don’t want our Shipment model to contain an ArraySlice, but rather a proper array of packages, we have to convert each of our two slices back into Array<Package> values — like this:
🌐
Objc
talk.objc.io › episodes › S01E23-splitting-arrays
Splitting Arrays - Swift Talk - objc.io
We'll duplicate our original test and modify it by removing the trailing code block in the input and the expected result. Now we can see that the test fails and we can fix our code. If the last element in our input array isn't a code block, we also need to flush our nonCodeBlocks array.
🌐
Apple Developer
developer.apple.com › documentation › swift › array › partition(by:)-90po8
partition(by:) | Apple Developer Documentation
Reorders the elements of the collection such that all the elements that match the given predicate are after all the elements that don’t match.
Top answer
1 of 5
32

You can make an extension so it can return an array of two arrays, working with Ints, Strings, etc:

extension Array {
    func split() -> [[Element]] {
        let ct = self.count
        let half = ct / 2
        let leftSplit = self[0 ..< half]
        let rightSplit = self[half ..< ct]
        return [Array(leftSplit), Array(rightSplit)]
    }
}

let deck = ["J", "Q", "K", "A"]
let nums = [0, 1, 2, 3, 4]

deck.split() // [["J", "Q"], ["K", "A"]]
nums.split() // [[0, 1], [2, 3, 4]]

But returning a named tuple is even better, because it enforces the fact that you expect exactly two arrays as a result:

extension Array {
    func split() -> (left: [Element], right: [Element]) {
        let ct = self.count
        let half = ct / 2
        let leftSplit = self[0 ..< half]
        let rightSplit = self[half ..< ct]
        return (left: Array(leftSplit), right: Array(rightSplit))
    }
}

let deck = ["J", "Q", "K", "A"]

let splitDeck = deck.split()
print(splitDeck.left) // ["J", "Q"]
print(splitDeck.right) // ["K", "A"]

Note: credits goes to Andrei and Qbyte for giving the first correct answer, I'm just adding info.

2 of 5
21

You can use subscript range

let deck: [String] = ["J", "Q", "K", "A"]

// use ArraySlice only for transient computation
let leftSplit: ArraySlice<String> = deck[0 ..< deck.count / 2] // "J", "Q"
let rightSplit: ArraySlice<String> = deck[deck.count / 2 ..< deck.count] // "K", "A"

// make arrays from ArraySlice
let leftDeck: [String] = Array(leftSplit) // "J", "Q"
let rightDeck: [String] = Array(rightSplit) // "K", "A"

EDIT: above code is for Swift 2, maybe for Swift 3 is a more convenient way.

Find elsewhere
Top answer
1 of 4
2

I offer you to use array with tuples (to hold keys). Compose this array and then you easily can remap it to format you need:

let array = ["23.88", "24", "30",  "24.16#C", "25#C", "12#C", "24.44#O", "50#O" , "31", "40" , "44#C", "55#C"]

let noModeTag = "#NoMode"

func group(array: [String]) -> [(String, [Double])]
{
    var result = [(String, [Double])]()

    func addNextElement(number: Double?, _ mode: String?) {
        guard let number = number, mode = mode else {fatalError("input format error")}
        if result.last?.0 == mode {
            var array = result.last!.1
            array.append(number)
            result[result.count - 1] = (mode, array)
        } else {
            result.append((mode, [number]))
        }
    }

    for element in array {
        if element.containsString("#") {
            let separated = element.componentsSeparatedByString("#")
            addNextElement(Double(separated.first ?? "_"), separated.last)
        } else {
            addNextElement(Double(element), noModeTag)
        }
    }
    return result
}

print(group(array))
//[("#NoMode", [23.88, 24.0, 30.0]), ("C", [24.16, 25.0, 12.0]), ("O", [24.44, 50.0]), ("#NoMode", [31.0, 40.0]), ("C", [44.0, 55.0])]
2 of 4
1

You'll need to modify the groupBy extension to group into an array of 2-tuples rather than an dictionary, where the first tuple element corresponds to a non-unique "key", and the 2nd tuple element is an array of subsequent elements in the self array that can be categorized to the given key.

Modified SequenceType extension

extension SequenceType {
    func groupBy<U : Comparable>(@noescape keyFunc: Generator.Element -> U) -> [(U,[Generator.Element])] {
        var tupArr: [(U,[Generator.Element])] = []
        for el in self {
            let key = keyFunc(el)
            if tupArr.last?.0 == key {
                tupArr[tupArr.endIndex-1].1.append(el)
            }
            else {
                tupArr.append((key,[el]))
            }
        }
        return tupArr
    }
}

Note also that is now suffices that the generic U in the extension conforms to Comparable, as we only use the U elements as "fake" keys in a tuple.

Call to extension

With this modification, we can call the groupBy(..) method as

let array = ["23.88", "24", "30",  "24.16#C", "25#C", "12", "24.44", "50" , "31#O", "40#O" , "44#C", "55#C"]

/* assuming we know the last character always describe the mode,
   given one is included (#) */
let groupedArray: [(String,[String])] = array.groupBy {
    guard $0.characters.contains("#") else { return "No mode" }
    return "mode = " + String($0.characters.last!)
}

print(groupedArray)
/* [("No mode", ["23.88", "24", "30"]), 
    ("mode = C", ["24.16#C", "25#C"]), 
    ("No mode", ["12", "24.44", "50"]), 
    ("mode = O", ["31#O", "40#O"]), 
    ("mode = C", ["44#C", "55#C"])] */

Removing original mode markings (#X) from grouped array

If you'd like to remove original mode markings (#X) in the resulting array, you can apply an additional map operation following the call to groupBy.

Removing markings with resulting values as String:

let groupedArrayClean = groupedArray.map { ($0.0, $0.1.map {
    String($0.characters.prefixUpTo($0.characters.indexOf("#") ?? $0.characters.endIndex))
    })
}

print(groupedArrayClean)
/* [("No mode", ["23.88", "24", "30"]), 
    ("mode = C", ["24.16", "25"]), 
    ("No mode", ["12", "24.44", "50"]), 
    ("mode = O", ["31", "40"]), 
    ("mode = C", ["44", "55"])] */

Or, with resulting values as Double:

let groupedArrayClean = groupedArray.map { ($0.0, $0.1.flatMap {
    Double(
        String(($0.characters.prefixUpTo($0.characters.indexOf("#") ?? $0.characters.endIndex))))
    })
}

print(groupedArrayClean)
/* [("No mode", [23.879999999999999, 24.0, 30.0]), 
    ("mode = C", [24.16, 25.0]), 
    ("No mode", [12.0, 24.440000000000001, 50.0]), 
    ("mode = O", [31.0, 40.0]), 
    ("mode = C", [44.0, 55.0])] */

Alternatively: group and clean up mode markings in single chained call

Or, both groupBy followed by map at once, without an intermediate assignment:

let groupedArrayClean: [(String,[String])] = array.groupBy {
    guard $0.characters.contains("#") else { return "No mode" }
    return "mode = " + String($0.characters.last!)
    }
    .map { ($0.0, $0.1.map {
        String($0.characters
            .prefixUpTo($0.characters.indexOf("#") ?? $0.characters.endIndex))
        })
}

(Analogously for the resulting Double values case.)

🌐
CopyProgramming
copyprogramming.com › howto › swift-swift-split-array-by-property-code-example
Swift Split Array by Property: Code Examples & Best Practices 2026
January 2, 2026 - The Dictionary(grouping:by:) initializer is the most straightforward and recommended way to split arrays by property. Introduced in Swift 5.0, this built-in method eliminates the need for manual loops and filtering.
🌐
GitHub
gist.github.com › ericdke › fa262bdece59ff786fcb
Swift: split array by chunks of given size · GitHub
extension Array { func chunks(_ chunkSize: Int) -> [[Element]] { return stride(from: 0, to: self.count, by: chunkSize).map { Array(self[$0..<Swift.min($0 + chunkSize, self.count)]) } } }
🌐
Programiz
programiz.com › swift-programming › library › string › split
Swift String split() (with Examples)
If not provided, there is no limit on the number of splits. omittingEmptySubsequences (optional) - specifies whether to omit empty string elements or to include them · Note: If maxSplits is specified, the array will have a maximum of maxSplits + 1 items. returns an array of substrings · var text = "Swift is awesome.
Top answer
1 of 13
57

In Swift 3/4 this would look like the following:

let numbers = ["1","2","3","4","5","6","7"]
let chunkSize = 2
let chunks = stride(from: 0, to: numbers.count, by: chunkSize).map {
    Array(numbers[$0..<min($0 + chunkSize, numbers.count)])
}
// prints as [["1", "2"], ["3", "4"], ["5", "6"], ["7"]]

As an extension to Array:

extension Array {
    func chunked(by chunkSize: Int) -> [[Element]] {
        return stride(from: 0, to: self.count, by: chunkSize).map {
            Array(self[$0..<Swift.min($0 + chunkSize, self.count)])
        }
    }
}

Or the slightly more verbose, yet more general:

let numbers = ["1","2","3","4","5","6","7"]
let chunkSize = 2
let chunks: [[String]] = stride(from: 0, to: numbers.count, by: chunkSize).map {
    let end = numbers.endIndex
    let chunkEnd = numbers.index($0, offsetBy: chunkSize, limitedBy: end) ?? end
    return Array(numbers[$0..<chunkEnd])
}

This is more general because I am making fewer assumptions about the type of the index into the collection. In the previous implementation I assumed that they could be could be compared and added.

Note that in Swift 3 the functionality of advancing indices has been transferred from the indices themselves to the collection.

2 of 13
45

With Swift 5, according to your needs, you can choose one of the five following ways in order to solve your problem.


1. Using AnyIterator in a Collection extension method

AnyIterator is a good candidate to iterate over the indices of an object that conforms to Collection protocol in order to return subsequences of this object. In a Collection protocol extension, you can declare a chunked(by:) method with the following implementation:

extension Collection {
    
    func chunked(by distance: Int) -> [[Element]] {
        precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop

        var index = startIndex
        let iterator: AnyIterator<Array<Element>> = AnyIterator({
            let newIndex = self.index(index, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex
            defer { index = newIndex }
            let range = index ..< newIndex
            return index != self.endIndex ? Array(self[range]) : nil
        })
        
        return Array(iterator)
    }
    
}

Usage:

let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]

2. Using stride(from:to:by:) function in an Array extension method

Array indices are of type Int and conform to Strideable protocol. Therefore, you can use stride(from:to:by:) and advanced(by:) with them. In an Array extension, you can declare a chunked(by:) method with the following implementation:

extension Array {
    
    func chunked(by distance: Int) -> [[Element]] {
        let indicesSequence = stride(from: startIndex, to: endIndex, by: distance)
        let array: [[Element]] = indicesSequence.map {
            let newIndex = $0.advanced(by: distance) > endIndex ? endIndex : $0.advanced(by: distance)
            //let newIndex = self.index($0, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex // also works
            return Array(self[$0 ..< newIndex])
        }
        return array
    }
    
}

Usage:

let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]

3. Using a recursive approach in an Array extension method

Based on Nate Cook recursive code, you can declare a chunked(by:) method in an Array extension with the following implementation:

extension Array {

    func chunked(by distance: Int) -> [[Element]] {
        precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop

        if self.count <= distance {
            return [self]
        } else {
            let head = [Array(self[0 ..< distance])]
            let tail = Array(self[distance ..< self.count])
            return head + tail.chunked(by: distance)
        }
    }
    
}

Usage:

let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]

4. Using a for loop and batches in a Collection extension method

Chris Eidhof and Florian Kugler show in Swift Talk #33 - Sequence & Iterator (Collections #2) video how to use a simple for loop to fill batches of sequence elements and append them on completion to an array. In a Sequence extension, you can declare a chunked(by:) method with the following implementation:

extension Collection {
    
    func chunked(by distance: Int) -> [[Element]] {
        var result: [[Element]] = []
        var batch: [Element] = []
        
        for element in self {
            batch.append(element)
            
            if batch.count == distance {
                result.append(batch)
                batch = []
            }
        }
        
        if !batch.isEmpty {
            result.append(batch)
        }
        
        return result
    }
    
}

Usage:

let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]

5. Using a custom struct that conforms to Sequence and IteratorProtocol protocols

If you don't want to create extensions of Sequence, Collection or Array, you can create a custom struct that conforms to Sequence and IteratorProtocol protocols. This struct should have the following implementation:

struct BatchSequence<T>: Sequence, IteratorProtocol {
    
    private let array: [T]
    private let distance: Int
    private var index = 0
    
    init(array: [T], distance: Int) {
        precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
        self.array = array
        self.distance = distance
    }
    
    mutating func next() -> [T]? {
        guard index < array.endIndex else { return nil }
        let newIndex = index.advanced(by: distance) > array.endIndex ? array.endIndex : index.advanced(by: distance)
        defer { index = newIndex }
        return Array(array[index ..< newIndex])
    }
    
}

Usage:

let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let batchSequence = BatchSequence(array: array, distance: 2)
let newArray = Array(batchSequence)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
🌐
Hacking with Swift
hackingwithswift.com › example-code › strings › how-to-split-a-string-into-an-array-componentsseparatedby
How to split a string into an array: components(separatedBy:) - free Swift example code and tips
May 28, 2019 - Swift version: 5.10 · Paul Hudson @twostraws May 28th 2019 · You can convert a string to an array by breaking it up by a substring using the components(separatedBy:) method. For example, you can split a string up by a comma and space like ...
🌐
DhiWise
dhiwise.com › post › breaking-it-down-swift-split-string-techniques-for-developer
Swift Split String Techniques Every Developer Should Know
July 10, 2024 - This code snippet limits the split to the first two spaces it encounters, resulting in an array of substrings with a maximum of three elements: ["Hello", "Swift!", "Let's split strings."] Here, the third element contains the remainder of the string because of the maxSplits parameter. When working with .split, Swift provides a default parameter called omittingEmptySubsequences. This is a Boolean parameter, set to true by default, which means that the method omits any empty substring that can occur due to consecutive separator elements.