Get Nth character in string with Swift
Easily get Nth character from string with Swift. In this post I will show you multiple ways to get the Nth character from a string with Swift
This is something that annoys me to this day with Swift. This is a simple task with other languages. JavaScript and Java can use charAt
and other languages like Python(actually JavaScript too) you can get a character using a simple index like you would with an array, eg, myString[2]
.
Apple has a reason for doing this as you can see here but even so, I find it annoying and I can never remember how to do it.
After researching how to do this I came across an StackOverflow post. This post has the correct answer, but I learned more than just getting a Character
from a String
. In this post I will go through the ways listed in the SO post to better understand it.
It seems that there was a way suggested in the String manifesto to do this, but unfortunately, that does not work anymore. This is the suggestion from the manifesto:
extension String : BidirectionalCollection {
subscript(i: Index) -> Character { return characters[i] }
}
String Extension
This solution creates a String
extension. This is what I would normally do, and what I was planning on writing this post about. Below is an example.
To get the nth
character using an extension, you can use the following code:
extension String {
subscript (characterIndex: Int) -> Character {
return self[index(startIndex, offsetBy: characterIndex)]
}
}
This is a simple extension that will get the Character
at a specific index and it works well, the issue I have with this is that it returns a Character
. Normally if I want to get the character at an index, I actually want the value to be a String
and not a Character
.
I updated it by creating a new instance of a string using the Character
that we would have returned in the above example. This is the updated code:
extension String {
subscript(characterIndex: Int) -> Self {
return String(self[index(startIndex, offsetBy: characterIndex)])
}
}
Substring
I must say, before today I didn't even know about Substring
in Swift. I have never really needed to use Substring
in Swift.
From research it seems that a Substring
might be more efficient. This is a snippet from the documentation on Substring
Operating on substrings is fast and efficient because a substring shares its storage with the original string
It seems like it might be a good idea to get the character as a Substring
. The other benefit is that a Substring
has the same interface as a String
, this is what the Apple documentation says:
The `Substring` type presents the same interface as `String`, so you can avoid or defer any copying of the string's contents
Now that we have this information we can do this:
extension String {
subscript(characterIndex: Int) -> Substring {
let start = index(startIndex, offsetBy: characterIndex)
let end = start
return self[start...end]
}
}
From my testing this gave me the same results as the previous two examples. There is one thing with using a Substring
and that can also be found in the documentation.
Important: Don't store substrings longer than you need them to perform a specific operation. A substring holds a reference to the entire storage of the string it comes from, not just to the portion it presents, even when there is no other reference to the original string. Storing substrings may, therefore, prolong the lifetime of string data that is no longer otherwise accessible, which can appear to be memory leakage.
StringProtocol
As I said, most of these solutions are based on solutions provided by the StackOverflow article I referenced in the beginning of this post.
Unlike Substring
, I had heard of StringProtocol
before, but I had never worked with it. According to StackOverflow, this is the best way to do it. So I took a look at the documentation for StringProtocol
and the first and only line of documentation says this:
A type that can represent a string as a collection of characters
Sounds like the perfect solution, so lets see what it looks like.
extension StringProtocol {
subscript(characterIndex: Int) -> String {
return String(self[index(startIndex, offsetBy: characterIndex)])
}
}
It is pretty much the same as the other solutions. I am showing the String
version, but you could return a character in the same way we did when we made the String
extension.
You could also do the Substring
method that we have gone through above, but you will need to make the Substring
return type an optional or force unwrap it when returning.
This is the code with the StringProtocol
returning a Substring
:
extension StringProtocol {
subscript(characterIndex: Int) -> Substring? {
let start = index(startIndex, offsetBy: characterIndex)
let end = start
return self[start...end] as? Substring
}
}
I personally prefer keeping the return value as optional, but if you want you could force unwrap it.
Conclusion
This was an interesting post for me. I never thought there would be so many techniques to get the nth character from a string.