aaazalea
aaazalea

Reputation: 7910

Indexing a string in Swift

I'm trying to find out how many letters 2 words (which are known to have the same length and to be in roman characters only) have in common. To do so, I have the following code

var sameplace = 0
for n in 0..<(word1.utf16Count){
    if word1[n] == word2[n]{
        sameplace += 1
    }
}

However, I get an error when trying to index the strings with n, which is an Int. The error is, specifically, 'Int' is not convertible to 'String.Index'.

Is there a way to index a string with an int, without having to convert it to an NSString every time first? Can I make the loop generate n as a String.Index instead of an Int? What's the most "swiftic" way to do this?

Upvotes: 2

Views: 776

Answers (3)

Martin R
Martin R

Reputation: 539685

A possible solution which does even work with all kinds of Unicode characters, grapheme clusters etc:

let word1 = "😄abcd🇩🇪"
let word2 = "😄axcy🇩🇪"

var sameplace = 0
for (c1, c2) in Zip2(word1, word2) {
   if c1 == c2 {
       sameplace++
   }
}

println(sameplace) // 4

A Swift String is also a Sequence of its characters, and Zip2 returns a sequence that iterates over the two given sequences in parallel.

Upvotes: 4

ahruss
ahruss

Reputation: 2130

So, these other answers work, too, but there is a way to do this which gets you pretty close to the way you want to do it:

var sameplace = 0
for n in 0..<(word1.utf16Count) {
    if Array(word1)[n] == Array(word2)[n] {
        sameplace += 1
    }
}

Upvotes: 0

drewag
drewag

Reputation: 94683

If you really need to access a character at a specific index, you can use the advance function with the startIndex of the string:

var sameplace = 0
for n in 0..<(word1.utf16Count){
    if word1[advance(word1.startIndex, n)] == word2[advance(word2.startIndex, n)]{
        sameplace += 1
    }
}

However, this is terribly inefficient because it iterates to n for every iteration through the for loop. It is also not ideal to assume the utf16 count is correct.

Instead, you can iterate the String.Indexs more manually:

var sameplace = 0
var index1 = word1.startIndex
var index2 = word2.startIndex
do {
    if word1[index1] == word2[index2]{
        sameplace += 1
    }
    index1 = index1.successor()
    index2 = index2.successor()
}
while(index1 != word1.endIndex && index2 != word2.endIndex);

You could also potentially make use of a templated function to help you iterate over two sequences (this will allow any Sequence, not just strings):

func iterateTwo<S: Sequence>(seq1: S, seq2: S, block: (S.GeneratorType.Element, S.GeneratorType.Element) -> ()) {
    var gen1 = seq1.generate()
    var gen2 = seq2.generate()
    while (true) {
        var possibleElement1 = gen1.next()
        var possibleElement2 = gen2.next()
        if possibleElement1 && possibleElement2 {
            block(possibleElement1!, possibleElement2!)
        }
        else {
            break
        }
    }
}

Then you can simply do:

var sameplace = 0
iterateTwo(word1, word2) { (char1, char2) in
    if char1 == char2 {
        sameplace += 1
    }
}

Upvotes: 3

Related Questions