Error with String endIndex in Swift - swift

I have the following code:
let hello = "Hola"
let indexI = hello.startIndex
let indexF = hello.endIndex
hello[indexI] // "H"
hello[hello.startIndex] // H
hello[hello.index(after: indexI)] // o
hello[indexF] // Fatal error: Can't form a Character from an empty String
But I have an error in hello[indexF]Why?

If you want to access the last element you need to replace:
let indexF = hello.endIndex
With:
let indexF = hello.index(before: hello.endIndex)
The documentation of endIndex says:
A string’s “past the end” position—that is, the position one greater
than the last valid subscript argument.

Related

Format String left of multiple characters in Swift 5?

I have some Strings that vary in length but always end in "listing(number)"
myString = 9AMnep8MAziUCK7VwKF51mXZ2listing28
.
I want to get the String without "listing(number)":
9AMnep8MAziUCK7VwKF51mXZ2
.
Methods I've tried such as .index(of: ) only let you format based off one character. Any simple solutions?
A possible solution is to search for the substring with Regular Expression and remove the result (replace it with empty string)
let myString = "9AMnep8MAziUCK7VwKF51mXZ2listing28"
let trimmedString = myString.replacingOccurrences(of: "listing\\d+$", with: "", options: .regularExpression)
\\d+ searches for one ore more digits
$ represents the end of the string
Alternatively without creating a new string
var myString = "9AMnep8MAziUCK7VwKF51mXZ2listing28"
if let range = myString.range(of: "listing\\d+$", options: .regularExpression) {
myString.removeSubrange(range)
}
Another option is to split the string in parts with "listing" as separator
let result = myString.components(separatedBy: "listing").first
So to solve your issue find the code below with few comments written to try and explain each steps have taken. kindly note i have modified or arrived at this solution using this links as a guide.
https://stackoverflow.com/a/40070835/6596443
https://www.dotnetperls.com/substring-swift
extension String {
//
// Paramter inputString: This is the string you want to manipulate
// Paramter- startStringOfUnwanted: This is the string you want to start the removal or replacement from
//return : The expected output you want but can be emptystring if unable to
static func trimUnWantedEndingString(inputString: String,startStringOfUnwanted: String) -> String{
//Output string
var outputString: String?
//Getting the range based on the string content
if let range = myString.range(of: startStringOfUnwanted) {
//Get the lowerbound of the range
let lower = range.lowerBound
//Get the upperbound of the range
let upper = range.upperBound
//Get the integer position of the start index of the unwanted string i added plus one to ensure it starts from the right position
let startPos = Int(myString.distance(from: myString.startIndex, to: lower))+1
//Get the integer position of the end index of the unwanted string i added plus one to ensure it starts from the right position
let endPos = Int(myString.distance(from: myString.startIndex, to: upper))+1
//Substract the start int from the end int to get the integer value that will be used to get the last string i want to stop trimming at
let endOffsetBy = endPos-startPos
//get thes string char ranges of values
let result = myString.index(myString.startIndex, offsetBy: 0)..<myString.index(myString.endIndex, offsetBy: -endOffsetBy)
//converts the results to string or get the string representation of the result and then assign it to the OutputString
outputString = String(myString[result]);
}
return outputString ?? "";
}
}
let myString = "9AMnep8MAziUCK7VwKF51mXZ2listing28"
String.trimUnWantedEndingString(inputString: myString, startStringOfUnwanted:"listing")

How to get SubString From Range in iOS Swift 4.2 [duplicate]

This question already has answers here:
Get nth character of a string in Swift
(47 answers)
Closed 3 years ago.
I am new to swift, I want to get substring from specified range. But I am getting some errors.
I have found similar questions on getting subString from range, but those couldn't work for me. I am using XCode 10.1 and swift 4.2. I have getting error while getting substring from specified range. I have tried like this (Sample Code)
let textStr = "Sample text here"
let newRange = NSMakeRange(4,9)
let startIndex = textStr?.index((textStr?.startIndex)!, offsetBy: newRange.location)
let endIndex = textStr?.index((textStr?.startIndex)!, offsetBy: newRange.length)
let newHashtagRange = startIndex..<endIndex
let newHashTagFound = textStr[newHashtagRange]
Error:
I am getting below error for this line of code
let newHashtagRange = startIndex..<endIndex
Binary operator '..<' cannot be applied to two 'String.Index?'
operands
I just struct here from last two days.
But in Objective-C just need one line of code like SubStringFromRange(range).
You are getting an error because both startIndex and endIndex are optionals due to the optional chain you have when defining them.
That is odd because, in your example code, textStr is not actually optional and the optional chain wouldn't even compile. My guess is you were attempting to shorten the example. I will assume textStr is meant to be optional and show you how to avoid using forced unwrapping which is almost always a bad idea. Also, you are getting the end index incorrectly. You need to do an offset from the calculated start index with the length. That leaves you with this:
let textStr: String? = "Sample text here"
if let textStr = textStr {
let newRange = NSMakeRange(4,9)
let startIndex = textStr.index(textStr.startIndex, offsetBy: newRange.location)
let endIndex = textStr.index(startIndex, offsetBy: newRange.length)
let newHashtagRange = startIndex..<endIndex
let newHashTagFound = textStr[newHashtagRange]
}
However, a Range can be initialized from an NSRange which is reliable and built in (it returns an optional in case the range is out of bounds):
let textStr: String? = "Sample text here"
let newRange = NSMakeRange(4,9)
if let textStr = textStr
, let newHashtagRange = Range(newRange, in: textStr)
{
let newHashTagFound = textStr[newHashtagRange]
}

String Indexes in swift

let greeting = "Hello World"
greeting[greeting.startIndex]
This code will print first character of string "H"
let = "Hello World"
greeting.startIndex
greeting.endIndex
whats the difference? this thing don't do anything. Even i dont get error for
greeting.endIndex
of course I am not retrieving string value through subscript syntax but in the Substring topic I found
greeting.endIndex
it is print only
string.index
Ok let me explain why I am asking about string.index and greeting.endIndex
let greeting = "Hello_world!"
let index = greeting.index(of: "_") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"
This code is related to substring, I totally understand what is it doing and in second line of code there is Nil-Coalescing Operator and you know what is it for. But what if
let greeting = "Hello world!"
let index = greeting.index(of: "_") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello world"
If there is no "_" in string value means greeting.index(of: "_") is nil then it should be returns default value greeting.endIndex as Nil-Coalescing Operator does right. So why does greeting.endIndex returning `"Hello world!"
Your code does nothing. This code however
let greeting = "Hello"
print(greeting[greeting.startIndex]) // Prints 'H'
print(greeting[greeting.endIndex])
Causes a fatal error because greeting.endIndex actually "points" to just after the end of the String, so the last statement is an array bounds violation.
In case you describe startIndex return position of the first character in a nonempty string and the endIndex return the position one greater than the last valid subscript argument.
Also in version 4 of the Swift. String is represented as Collection.
So when you do:
greeting[greeting.startIndex]
You ask greeting string to return element in the first position that is "H"
The startIndex is the start of the string. The endIndex is the end of the string. You can use these when building ranges or when creating new indexes as an offsetBy from one or both of these.
So, for example, if you want a Substring that excluded the first and last characters, you could set fromIndex to be startIndex plus 1 and toIndex to be endIndex less 1, yielding:
let string = "Hello World"
let fromIndex = string.index(string.startIndex, offsetBy: 1)
let toIndex = string.index(string.endIndex, offsetBy: -1)
let substring = string[fromIndex..<toIndex] // "ello Worl"
You then ask:
If there is no "_" in string value means greeting.index(of: "_") is nil then it should be returns default value greeting.endIndex as Nil-Coalescing Operator does right. So why does greeting.endIndex returning `"Hello world!"
Make sure you don't conflate the string[index] syntax (which returns a Character) and the string[..<index] which returns a Substring from the startIndex up to index (i.e. "return Substring of everything up to index"):
let beginning = greeting[..<index]
This partially bound range is just short-hand for:
let beginning = greeting[greeting.startIndex..<index]
So, when you default index to the greeting.endIndex, that's like saying you want a substring that is, effectively, the whole string:
let beginning = greeting[greeting.startIndex..<greeting.endIndex]
So, that's why the syntax presented in your question, greeting[..<index], returns the whole string if _ was not found and it used endIndex.
As an aside, if you wanted different behavior, namely for it to return an empty substring if the _ is not found, you could do the following:
let index = greeting.index(of: "_") ?? greeting.startIndex
let beginning = greeting[..<index]
Or, if you think that's a little too cute, just adopt standard safe unwrapping patterns, e.g.:
guard let index = greeting.index(of: "_") else {
// handle unfound _ here
}
let beginning = greeting[..<index]

New substring method swift 3 [duplicate]

I'm using the following code:
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
Since update to Xcode 7.3 (Swift 2.2) I got the following hint:
'init(start:end:)' is deprecated: it will be removed in Swift 3. Use
the '..<' operator.
For me is not clear how to "translate" it correctly with "using the '..<' operator.
You should simply write
var continousDigitsRange1:Range<Int> = 0..<0
or if you want to go even simpler
var continousDigitsRange = 0..<0
Also worth noting, to substringWithRange a String, you can now use
let theString = "Hello, how are you"
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
theString.substringWithRange(range)
The closed range operator (a...b) defines a range that runs from a
to b, and includes the values a and b. The value of a must not be
greater than b.
The half-open range operator (a..<b) defines a range that runs from a
to b, but does not include b. It is said to be half-open because it
contains its first value, but not its final value. As with the closed
range operator, the value of a must not be greater than b. If the
value of a is equal to b, then the resulting range will be empty.
The Swift Programming Language (Swift 2.2) - Basic Operators
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
--to--
var continousDigitsRange:Range<Int> = 0..<0
to show bmichotte's answer in full...
let theString = "Hello, how are you today my friend"
let start = 3
let end = 15
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
let p = theString.substringWithRange(range)
print("this is the middle bit>\(p)<")
this produces this is the middle bit>lo, how are <
Adding some points with reference to swift 3.0
//Countable Range Example.
let range1 = 0..<5
Countable Closed Range Example
let range2 = 0...5
//Range from bounds
let range = Range(uncheckedBounds: (range1.lowerBound,range1.upperBound))
//To get the distance from substringRange.
let str = "Hello, how are you"
let substringRange = str.characters.indices
// Below Swift 3.0
let length = substringRange.distance(from: substringRange.startIndex, to: substringRange.endIndex)
//For Swift 3.0
let length2 = str.distance(from: substringRange.startIndex, to: substringRange.endIndex)
I have always had a function to get the substring range of a string. Here is my updated function for Swift 3:
func getSubStringRange(fullString: String, fromIndex: Int, subStringSize: Int) -> Range<String.Index> {
let startIndex = fullString.characters.index(fullString.startIndex, offsetBy: fromIndex)
let endIndex = fullString.characters.index(startIndex, offsetBy: subStringSize)
let subStringRange = startIndex..<endIndex
return subStringRange
}
The function is pretty self explanatory - You pass in a string(fullString), the index of that string where the substring starts(fromIndex) and how big the subString is(subStringSize).
Example:
let greeting = "Hi, my name is Nathaniel"
let getName = greeting[getSubStringRange(fullString: greeting, fromIndex: 15, subStringSize: 9)]
print("Name: \(getName)")
-> Prints: "Name: Nathaniel"

'init(start:end:)' is deprecated: it will be removed in Swift 3. Use the '..<' operator

I'm using the following code:
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
Since update to Xcode 7.3 (Swift 2.2) I got the following hint:
'init(start:end:)' is deprecated: it will be removed in Swift 3. Use
the '..<' operator.
For me is not clear how to "translate" it correctly with "using the '..<' operator.
You should simply write
var continousDigitsRange1:Range<Int> = 0..<0
or if you want to go even simpler
var continousDigitsRange = 0..<0
Also worth noting, to substringWithRange a String, you can now use
let theString = "Hello, how are you"
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
theString.substringWithRange(range)
The closed range operator (a...b) defines a range that runs from a
to b, and includes the values a and b. The value of a must not be
greater than b.
The half-open range operator (a..<b) defines a range that runs from a
to b, but does not include b. It is said to be half-open because it
contains its first value, but not its final value. As with the closed
range operator, the value of a must not be greater than b. If the
value of a is equal to b, then the resulting range will be empty.
The Swift Programming Language (Swift 2.2) - Basic Operators
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
--to--
var continousDigitsRange:Range<Int> = 0..<0
to show bmichotte's answer in full...
let theString = "Hello, how are you today my friend"
let start = 3
let end = 15
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
let p = theString.substringWithRange(range)
print("this is the middle bit>\(p)<")
this produces this is the middle bit>lo, how are <
Adding some points with reference to swift 3.0
//Countable Range Example.
let range1 = 0..<5
Countable Closed Range Example
let range2 = 0...5
//Range from bounds
let range = Range(uncheckedBounds: (range1.lowerBound,range1.upperBound))
//To get the distance from substringRange.
let str = "Hello, how are you"
let substringRange = str.characters.indices
// Below Swift 3.0
let length = substringRange.distance(from: substringRange.startIndex, to: substringRange.endIndex)
//For Swift 3.0
let length2 = str.distance(from: substringRange.startIndex, to: substringRange.endIndex)
I have always had a function to get the substring range of a string. Here is my updated function for Swift 3:
func getSubStringRange(fullString: String, fromIndex: Int, subStringSize: Int) -> Range<String.Index> {
let startIndex = fullString.characters.index(fullString.startIndex, offsetBy: fromIndex)
let endIndex = fullString.characters.index(startIndex, offsetBy: subStringSize)
let subStringRange = startIndex..<endIndex
return subStringRange
}
The function is pretty self explanatory - You pass in a string(fullString), the index of that string where the substring starts(fromIndex) and how big the subString is(subStringSize).
Example:
let greeting = "Hi, my name is Nathaniel"
let getName = greeting[getSubStringRange(fullString: greeting, fromIndex: 15, subStringSize: 9)]
print("Name: \(getName)")
-> Prints: "Name: Nathaniel"