Skip to content

Instantly share code, notes, and snippets.

@robertmryan
Created December 4, 2024 21:35
Show Gist options
  • Save robertmryan/d7eadeec08b573c5add9727645897e0e to your computer and use it in GitHub Desktop.
Save robertmryan/d7eadeec08b573c5add9727645897e0e to your computer and use it in GitHub Desktop.
extension StringProtocol {
func split(into length: Int) -> [SubSequence] {
let count = self.count
return stride(from: 0, to: count, by: length).map { i in
let start = index(startIndex, offsetBy: i)
let end = index(startIndex, offsetBy: Swift.min(i + length, count))
return self[start ..< end]
}
}
}
@robertmryan
Copy link
Author

robertmryan commented Dec 4, 2024

Then you can do things like:

let string = "123456789012345678901234567890123456789012345"

string.split(into: 10).forEach { substring in
    logger.info("\(substring, privacy: .public)")
}

@robertmryan
Copy link
Author

robertmryan commented Dec 7, 2024

While I tried to make this generic, using StringProtocol, to support Substring, we could make this even a little more generic, to split any Collection type into SubSequences of a certain length. Admittedly, this is not immediately relevant to the question of “how do I split strings into substrings with a maximum length”, but just an academic observation:

extension Collection {
    func split(into length: Int) -> [SubSequence] {
        let count = self.count

        return stride(from: 0, to: count, by: length).map { i in
            let start = index(startIndex, offsetBy: i)
            let end = index(start, offsetBy: Swift.min(length, count - i))
            return self[start ..< end]
        }
    }
}

Or, technically, this might be more efficient:

extension Collection {
    func split(into length: Int) -> [SubSequence] {
        let count = self.count
        var subsequences: [SubSequence] = []
        
        var (subsequenceCount, remainder) = count.quotientAndRemainder(dividingBy: length)
        if remainder != 0 { subsequenceCount += 1}
        
        subsequences.reserveCapacity(subsequenceCount)
        
        var subsequenceStart = startIndex
        var startOffset = 0
        
        while startOffset < count {
            let subsequenceLength = Swift.min(startOffset + length, count) - startOffset
            let subsequenceEnd = index(subsequenceStart, offsetBy: subsequenceLength)
            subsequences.append(self[subsequenceStart ..< subsequenceEnd])
            startOffset += subsequenceLength
            subsequenceStart = subsequenceEnd
        }
        
        return subsequences
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment