How to convert "Index" to type "Int" in Swift?

StringSwift

String Problem Overview


I want to convert the index of a letter contained within a string to an integer value. Attempted to read the header files but I cannot find the type for Index, although it appears to conform to protocol ForwardIndexType with methods (e.g. distanceTo).

var letters = "abcdefg"
let index = letters.characters.indexOf("c")!

// ERROR: Cannot invoke initializer for type 'Int' with an argument list of type '(String.CharacterView.Index)'
let intValue = Int(index)  // I want the integer value of the index (e.g. 2)

Any help is appreciated.

String Solutions


Solution 1 - String

edit/update:

Xcode 11 • Swift 5.1 or later

extension StringProtocol {
    func distance(of element: Element) -> Int? { firstIndex(of: element)?.distance(in: self) }
    func distance<S: StringProtocol>(of string: S) -> Int? { range(of: string)?.lowerBound.distance(in: self) }
}

extension Collection {
    func distance(to index: Index) -> Int { distance(from: startIndex, to: index) }
}

extension String.Index {
    func distance<S: StringProtocol>(in string: S) -> Int { string.distance(to: self) }
}

Playground testing

let letters = "abcdefg"

let char: Character = "c"
if let distance = letters.distance(of: char) {
    print("character \(char) was found at position #\(distance)")   // "character c was found at position #2\n"
} else {
    print("character \(char) was not found")
}

let string = "cde"
if let distance = letters.distance(of: string) {
    print("string \(string) was found at position #\(distance)")   // "string cde was found at position #2\n"
} else {
    print("string \(string) was not found")
}

Solution 2 - String

Works for Xcode 13 and Swift 5

let myString = "Hello World"

if let i = myString.firstIndex(of: "o") {
  let index: Int = myString.distance(from: myString.startIndex, to: i)
  print(index) // Prints 4
}

The function func distance(from start: String.Index, to end: String.Index) -> String.IndexDistance returns an IndexDistance which is just a typealias for Int

Solution 3 - String

Swift 4

var str = "abcdefg"
let index = str.index(of: "c")?.encodedOffset // Result: 2

Note: If String contains same multiple characters, it will just get the nearest one from left

var str = "abcdefgc"
let index = str.index(of: "c")?.encodedOffset // Result: 2

Solution 4 - String

encodedOffset has deprecated from Swift 4.2.

Deprecation message: encodedOffset has been deprecated as most common usage is incorrect. Use utf16Offset(in:) to achieve the same behavior.

So we can use utf16Offset(in:) like this:

var str = "abcdefgc"
let index = str.index(of: "c")?.utf16Offset(in: str) // Result: 2

Solution 5 - String

When searching for index like this

⛔️ guard let index = (positions.firstIndex { position <= $0 }) else {

it is treated as Array.Index. You have to give compiler a clue you want an integer

 guard let index: Int = (positions.firstIndex { position <= $0 }) else {

Solution 6 - String

Swift 5

You can do convert to array of characters and then use advanced(by:) to convert to integer.

let myString = "Hello World"

if let i = Array(myString).firstIndex(of: "o") {
  let index: Int = i.advanced(by: 0)
  print(index) // Prints 4
}

Solution 7 - String

To perform string operation based on index , you can not do it with traditional index numeric approach. because swift.index is retrieved by the indices function and it is not in the Int type. Even though String is an array of characters, still we can't read element by index.

This is frustrating.

So ,to create new substring of every even character of string , check below code.

let mystr = "abcdefghijklmnopqrstuvwxyz"
let mystrArray = Array(mystr)
let strLength = mystrArray.count
var resultStrArray : [Character] = []
var i = 0
while i < strLength {
    if i % 2 == 0 {
        resultStrArray.append(mystrArray[i])
      }
    i += 1
}
let resultString = String(resultStrArray)
print(resultString)

Output : acegikmoqsuwy

Thanks In advance

Solution 8 - String

Here is an extension that will let you access the bounds of a substring as Ints instead of String.Index values:

import Foundation

/// This extension is available at
/// https://gist.github.com/zackdotcomputer/9d83f4d48af7127cd0bea427b4d6d61b
extension StringProtocol {
    /// Access the range of the search string as integer indices
    /// in the rendered string.
    /// - NOTE: This is "unsafe" because it may not return what you expect if
    ///     your string contains single symbols formed from multiple scalars.
    /// - Returns: A `CountableRange<Int>` that will align with the Swift String.Index
    ///     from the result of the standard function range(of:).
    func countableRange<SearchType: StringProtocol>(
        of search: SearchType,
        options: String.CompareOptions = [],
        range: Range<String.Index>? = nil,
        locale: Locale? = nil
    ) -> CountableRange<Int>? {
        guard let trueRange = self.range(of: search, options: options, range: range, locale: locale) else {
            return nil
        }

        let intStart = self.distance(from: startIndex, to: trueRange.lowerBound)
        let intEnd = self.distance(from: trueRange.lowerBound, to: trueRange.upperBound) + intStart

        return Range(uncheckedBounds: (lower: intStart, upper: intEnd))
    }
}

Just be aware that this can lead to weirdness, which is why Apple has chosen to make it hard. (Though that's a debatable design decision - hiding a dangerous thing by just making it hard...)

You can read more in the String documentation from Apple, but the tldr is that it stems from the fact that these "indices" are actually implementation-specific. They represent the indices into the string after it has been rendered by the OS, and so can shift from OS-to-OS depending on what version of the Unicode spec is being used. This means that accessing values by index is no longer a constant-time operation, because the UTF spec has to be run over the data to determine the right place in the string. These indices will also not line up with the values generated by NSString, if you bridge to it, or with the indices into the underlying UTF scalars. Caveat developer.

Solution 9 - String

In case you got an "index is out of bounds" error. You may try this approach. Working in Swift 5

extension String{

   func countIndex(_ char:Character) -> Int{
        var count = 0
        var temp = self
  
        for c in self{
            
            if c == char {
               
                //temp.remove(at: temp.index(temp.startIndex,offsetBy:count))
                //temp.insert(".", at: temp.index(temp.startIndex,offsetBy: count))
                
                return count

            }
            count += 1
         }
        return -1
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChristopherView Question on Stackoverflow
Solution 1 - StringLeo DabusView Answer on Stackoverflow
Solution 2 - StringEric33187View Answer on Stackoverflow
Solution 3 - StringShem Alexis ChavezView Answer on Stackoverflow
Solution 4 - StringShohinView Answer on Stackoverflow
Solution 5 - StringmilcziView Answer on Stackoverflow
Solution 6 - StringNick McConnellView Answer on Stackoverflow
Solution 7 - StringShrikant PhadkeView Answer on Stackoverflow
Solution 8 - StringZackView Answer on Stackoverflow
Solution 9 - StringWilliam TongView Answer on Stackoverflow