Subscripting a string should be possible or have an easy alternative

Maybe you have fun with this:

import Foundation

public struct IndexedString: CustomStringConvertible {
    
    private let s: String
    private let indices: [Int]
    private let utf8: [UInt8]
    
    public var description: String { s }
    
    public var count: Int { indices.count }
    
    public init(_ s: String) {
        self.s = s
        var index = 0
        indices = s.map{ index += $0.utf8.count; return index }
        utf8 = Array(s.utf8)
    }
    
    public subscript(position: Int) -> IndexedString {
        return IndexedString(String(bytes:  utf8[(position > 0 ? indices[position-1] : 0)..<indices[position]], encoding: .utf8)!)
    }
    
    public subscript(range: Range<Int>) -> IndexedString {
        return IndexedString(String(bytes:  utf8[(range.lowerBound > 0 ? indices[range.lowerBound-1] : 0)..<indices[range.upperBound-1]], encoding: .utf8)!)
    }
    
    public subscript(range: ClosedRange<Int>) -> IndexedString {
        return IndexedString(String(bytes:  utf8[(range.lowerBound > 0 ? indices[range.lowerBound-1] : 0)..<indices[range.upperBound]], encoding: .utf8)!)
    }
    
    public func replacing<Replacement>(_ regex: some RegexComponent, with replacement: Replacement, maxReplacements: Int = .max) -> Self where Replacement : Collection, Replacement.Element == Character {
        IndexedString(s.replacing(regex, with: replacement, maxReplacements: maxReplacements))
    }
}

// usage:

let s = IndexedString("Häl😉y\u{301}o")
print(s) // prints "Häl😉ýo"
for i in 0..<s.count {
    print("\(i): \(s[i])") // prints "0: H", "1: ä", "2: l", "3: 😉", "4: ý", and "5: o"
}
print(s[1..<3]) // prints "äl"
print(s[1...3]) // prints "äl😉"
print(s.replacing(/[a-z]/, with: "x")) // prints "Häx😉ýx"
1 Like