Hassan Taleb
Hassan Taleb

Reputation: 2546

Subtract multiple ranges from one range

How can we subtract multiple ranges of type Range<String.Index> from one range of type Range<String.Index>?

Example:

let str = "let <#name#> = <#value#>"
let range = str.startIndex..<str.endIndex

//the range of str is {0, 24}
//the range of <#name#> is {4, 8}
//the range of <#value#> is {15, 9}

How can we subtract the ranges {4, 8} and {15, 9} from the range of str to obtain the ranges of "let " and " = "

Upvotes: 1

Views: 234

Answers (1)

HangarRash
HangarRash

Reputation: 15046

Here's an extension to StringProtocol that will return an array of ranges from a string that are outside the passed in ranges:

extension StringProtocol {
    func ranges(outsideOf ranges: [Range<String.Index>]) -> [Range<String.Index>] {
        var res = [Range<String.Index>]()

        var lastEnd = self.startIndex

        // Iterate each provided range (sorted by lowerBound)
        for range in ranges.sorted(by: { $0.lowerBound < $1.lowerBound }) {
            // Added the result if this range has space before the previous range
            if range.lowerBound > lastEnd {
                res.append(Range(uncheckedBounds: (lastEnd, range.lowerBound)))
            }
            if range.upperBound > lastEnd {
                lastEnd = range.upperBound
            }
        }

        // If there's any space after the last range, add that to the result
        if lastEnd < self.endIndex {
            res.append(Range(uncheckedBounds: (lastEnd, self.endIndex)))
        }

        return res
    }
}

Here's some sample code to demonstrate its use:

let str = "let <@name@> = <@value@>"
let nameRng = str.range(of: "<@name@>")!
let valueRng = str.range(of: "<@value@>")!
let ranges = str.ranges(outsideOf: [nameRng, valueRng])
print(ranges.map { str[$0] })

Output:

["let ", " = "]

Upvotes: 1

Related Questions