Why Does Swift Gives an Error When I Set The Offsetby Value Equal to Endindex Value in The Index(_, Offsetby, Limitedby) Function

Why does swift gives an error when I set the offsetBy value equal to endIndex value in the index(_ , offsetBy , limitedBy) function?

The problem is that the limit given to index(_:offsetBy:limitedBy:) is an inclusive bound for the result. Therefore with

let someString = "hello"

the index returned by

someString.index(someString.startIndex, offsetBy: 5, limitedBy: someString.endIndex)

will be someString.endIndex, which is a past the end index, and therefore not a valid index to subscript the string with.

A simple solution therefore would be to just add a condition to your if statement to check that the index you get back isn't the endIndex:

let someString = "hello"
let offset = 5

if let someIndex = someString.index(someString.startIndex,
offsetBy: offset,
limitedBy: someString.endIndex
), someIndex != someString.endIndex {
print(someString[someIndex])
}

Or a nicer option would be to use the CharacterView's indices property in order to get a collection of valid indices to subscript with (excluding endIndex), and use dropFirst(_:) and first in order to get the index at the given offset:

if let index = someString.characters.indices.dropFirst(offset).first {
print(someString[index])
}

This takes advantage of the fact that dropFirst(_:) takes an upper bound of elements to drop, returning an empty subsequence if it's larger than the collection's count, as well as the fact that first returns nil for an empty collection.

How does String.Index work in Swift

Sample Image

All of the following examples use

var str = "Hello, playground"

startIndex and endIndex

  • startIndex is the index of the first character
  • endIndex is the index after the last character.

Example

// character
str[str.startIndex] // H
str[str.endIndex] // error: after last character

// range
let range = str.startIndex..<str.endIndex
str[range] // "Hello, playground"

With Swift 4's one-sided ranges, the range can be simplified to one of the following forms.

let range = str.startIndex...
let range = ..<str.endIndex

I will use the full form in the follow examples for the sake of clarity, but for the sake of readability, you will probably want to use the one-sided ranges in your code.

after

As in: index(after: String.Index)

  • after refers to the index of the character directly after the given index.

Examples

// character
let index = str.index(after: str.startIndex)
str[index] // "e"

// range
let range = str.index(after: str.startIndex)..<str.endIndex
str[range] // "ello, playground"

before

As in: index(before: String.Index)

  • before refers to the index of the character directly before the given index.

Examples

// character
let index = str.index(before: str.endIndex)
str[index] // d

// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range] // Hello, playgroun

offsetBy

As in: index(String.Index, offsetBy: String.IndexDistance)

  • The offsetBy value can be positive or negative and starts from the given index. Although it is of the type String.IndexDistance, you can give it an Int.

Examples

// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index] // p

// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
str[range] // play

limitedBy

As in: index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

  • The limitedBy is useful for making sure that the offset does not cause the index to go out of bounds. It is a bounding index. Since it is possible for the offset to exceed the limit, this method returns an Optional. It returns nil if the index is out of bounds.

Example

// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
str[index] // p
}

If the offset had been 77 instead of 7, then the if statement would have been skipped.

Why is String.Index needed?

It would be much easier to use an Int index for Strings. The reason that you have to create a new String.Index for every String is that Characters in Swift are not all the same length under the hood. A single Swift Character might be composed of one, two, or even more Unicode code points. Thus each unique String must calculate the indexes of its Characters.

It is possible to hide this complexity behind an Int index extension, but I am reluctant to do so. It is good to be reminded of what is actually happening.

Xcode Playground bug? fatal error: Can't form a Character from an empty String

The problem is from the expression s[idx] when idx is too large. The error goes away when you update the calculation of idx to:

if let idx = s.index(s.startIndex, offsetBy: i + 1, limitedBy: s.index(s.endIndex, offsetBy: -1)) {

or, as kindly suggested by Leo,

if let idx = s.index(s.startIndex, offsetBy: i + 1, limitedBy: s.index(before: s.endIndex)) {

Runtime Error when Trying Bottom Up Approach to Implement Fibonacci function in Swift?

You have no recursion in your code.

Note that Fib(35) is too small for integer overflow, but Fib(100) definitely is too large both for 32-bit and for 64-bit integers, so your compiler catches integer overflow error.

You may set limit n=41 for 32-bit signed ints and n=92 for 64-bit signed ints.

Higher values require using of long/arbitrary precision integer arithmetics.

value(forKey:) for NSObject not working in Swift 4

Key-value coding requires Objective-C. In Swift 4 you have to mark members accessible from Obj-C explicitly:

@objcMembers
class Person: NSObject {

or

class Person: NSObject {
@objc var name = ""
var age = 0
}

See Swift Evolution 160

How to use do-catch functions in Swift

You can just add throws to your method signature and throw a custom error. I would also make the method generic and extend StringProtocol instead of String to support Substring as well. Note that constraining to RangeReplaceableCollection is required to be able to use mutating func insert<S>(contentsOf newElements: S, at i: Index) where S: Collection, Element == S.Element:

extension String {
enum Error: Swift.Error {
case invalidIndexDistance
}
}


extension StringProtocol where Self: RangeReplaceableCollection {
mutating func insert<S: StringProtocol>(contentsOf string: S, at distance: Int) throws {
guard let index = self.index(startIndex, offsetBy: distance, limitedBy: endIndex) else {
throw String.Error.invalidIndexDistance
}
insert(contentsOf: string, at: index)
}
}


var substring = "abcde".dropFirst()
do {
try substring.insert(contentsOf: "fgh", at: 2)
substring // "bcfghde"
} catch {
print(error)
}

NSLinguisticTaggerOptions in swift

For the options parameter, try Int(opts.toRaw()).

(The "3" that you tried worked because it is the raw version.)

My code in Swift vs. Java. Swift gives an error but Java doesn't. Are there any differences?

No, apple didn't get rid of the for in loops.

Your problem is, that line:

 for l in 2...i-1 {

Because if i-1 is less than 2, this error occurs. So you need to make a check, if i-1 is equal or greater than 2. check this code to prove the error:

for l in 2...2 { //No error
for l in 2...1 { //error

So I would make something like that, if you want to keep your code:

if(i-1 >= 2){
for a in 2...i-1 {
if ((i%a) == 0) {
isPrimeFactor = false
}//end if
}//end for
}

Also you have an error here:

 if (isPrimeFactor == true) {
i
}//end if

You need to either wrap the i into an println(i) or remove it.



Related Topics



Leave a reply



Submit