How to figure out the range of a substring in one string and then use it in another

Viewed 77

Basicly I want something like this,

NSString* foobar(NSString *input) {
    // say input is "1"
    NSString *string = @"0123456789";
    NSString *anotherString = @"零一二三四五六七八九";
    
    NSRange range = [string rangeOfString:input];
    
    // return "一" here
    return [anotherString substringWithRange:range];
}

I tried the same stuff in Swift,

func foobar(input: String) -> String {
    // say input is "1"
    let string = "0123456789"
    
    let range = string.range(of: input, options: .anchored)
    let result = anotherString[range!]
    
    // return "012" here
    return String(result)
}

why? And how can I achieve this?

2 Answers

String (or generally, collection) indices must only be used with the collection that they were created with. In order to find the same positions in another string, the indices must be converted to (integer) offsets and back to indices of the target string:

func foobar(input: String) -> String? {
    let s1 = "0123456789"
    let s2 = "一二三四五六七八九";

    guard let range = s1.range(of: input) else {
        return nil
    }
    let pos = s1.distance(from: s1.startIndex, to: range.lowerBound)
    let len = s1.distance(from: range.lowerBound, to: range.upperBound)
    guard
        let lo = s2.index(s2.startIndex, offsetBy: pos, limitedBy: s2.endIndex),
        let hi = s2.index(lo, offsetBy: len, limitedBy: s2.endIndex)
    else {
        return nil
    }
    return String(s2[lo..<hi])
}

print(foobar(input: "1") as Any) // Optional("一")
print(foobar(input: "123") as Any) // Optional("一二三")
print(foobar(input: "124") as Any) // nil

Your Objective-C code works as long as all characters in the string consume a single UTF-16 code unit (because that is what NSRange counts). It will not work correctly emojis, flags, and other characters which are represented as UTF-16 surrogate pairs, e.g. with

NSString *anotherString = @"一二三四五六七八九";

Another approach is converting strings to array of characters

func find(_ str: Character) {
    let firstArr = Array("0123456789")
    let secondArr = Array("零一二三四五六七八九")
    guard let index = firstArr.firstIndex(of: str) else {
        print("Not found")
        return
    }
    print(firstArr[index]) // 2
    print(secondArr[index]) // 二
}
find("2")
Related