Swift: second occurrence with indexOf
- List item
You can perform another search for the index of element at the remaining array slice as follow:
edit/update: Swift 5.2 or later
extension Collection where Element: Equatable {
/// Returns the second index where the specified value appears in the collection.
func secondIndex(of element: Element) -> Index? {
guard let index = firstIndex(of: element) else { return nil }
return self[self.index(after: index)...].firstIndex(of: element)
}
}
extension Collection {
/// Returns the second index in which an element of the collection satisfies the given predicate.
func secondIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
guard let index = try firstIndex(where: predicate) else { return nil }
return try self[self.index(after: index)...].firstIndex(where: predicate)
}
}
Testing:
let numbers = [1,3,4,5,5,9,0,1]
if let index = numbers.secondIndex(of: 5) {
print(index) // "4\n"
} else {
print("not found")
}
if let index = numbers.secondIndex(where: { $0.isMultiple(of: 3) }) {
print(index) // "5\n"
} else {
print("not found")
}
Find index of Nth instance of substring in string in Swift
Xcode 11 • Swift 5 or later
let sentence = "hey hi hello, hey hi hello"
let query = "hello"
var searchRange = sentence.startIndex..<sentence.endIndex
var indices: [String.Index] = []
while let range = sentence.range(of: query, options: .caseInsensitive, range: searchRange) {
searchRange = range.upperBound..<searchRange.upperBound
indices.append(range.lowerBound)
}
print(indices) // "[7, 21]\n"
Index or range of second ocurence of bytes in file
You can find the second occurrence this way :
if let rg1 = data.range(of: mtrkChunk),
let rg2 = data[rg1.upperBound...].range(of: mtrkChunk) {
print(rg2)
}
Get the first index in a custom indexOf function in Swift 4
Your syntax with extensions is fine, so you need improvement on your algorithm logic. Break this down into two simpler problems: checking for a string within a substring, and checking if a string matches permutations of a string
What if we could compare two strings regardless of order instead of checking all permutations? If we could create a function that could return true as long as two strings had the same occurrence of letters, we wouldn't need all the permutations. Let's call this function funA(String) -> Bool
We could then call that function on a moving index within the String we're checking against (or in the case of an extension, self)
Example:
Text: Hello there
Phrase: lol
Starting index: 0, length: 3
funA(Hel) = false
funA(ell) = false
funA(llo) = true
return 2, which is the current starting index
Finding indices for all instances of element in array
You can create your own indices method that takes a predicate as parameter:
Xcode 11 • Swift 5.1
extension Collection where Element: Equatable {
func indices(of element: Element) -> [Index] { indices.filter { self[$0] == element } }
}
extension Collection {
func indices(where isIncluded: (Element) throws -> Bool) rethrows -> [Index] { try indices.filter { try isIncluded(self[$0]) } }
}
let arr = [1, 2, 3, 1, 0, 1, 2, 2, 3, 1, 1, 2]
let search = 1
let indices = arr.indices(where: { $0 == search })
// or simply
// let indices = arr.indices { $0 == search }
print(indices) // [0, 3, 5, 9, 10]
let indices2 = arr.indices(of: search)
print(indices2) // [0, 3, 5, 9, 10]
let string = "Hello World !!!"
let indices3 = string.indices(of: "o")
print(indices3) // [Swift.String.Index(_compoundOffset: 16, _cache: Swift.String.Index._Cache.character(1)), Swift.String.Index(_compoundOffset: 28, _cache: Swift.String.Index._Cache.character(1))]
How to get index(of:) to return multiple indices?
You might use a combination of enumerated
and compactMap
:
let indexArray = group.enumerated().compactMap {
$0.element == "A" ? $0.offset : nil
}
print(indexArray) // [10, 12, 13, 27]
Swift find all occurrences of a substring
You just keep advancing the search range until you can't find any more instances of the substring:
extension String {
func indicesOf(string: String) -> [Int] {
var indices = [Int]()
var searchStartIndex = self.startIndex
while searchStartIndex < self.endIndex,
let range = self.range(of: string, range: searchStartIndex..<self.endIndex),
!range.isEmpty
{
let index = distance(from: self.startIndex, to: range.lowerBound)
indices.append(index)
searchStartIndex = range.upperBound
}
return indices
}
}
let keyword = "a"
let html = "aaaa"
let indicies = html.indicesOf(string: keyword)
print(indicies) // [0, 1, 2, 3]
Finding index of character in Swift String
You are not the only one who couldn't find the solution.
String
doesn't implement RandomAccessIndexType
. Probably because they enable characters with different byte lengths. That's why we have to use string.characters.count
(count
or countElements
in Swift 1.x) to get the number of characters. That also applies to positions. The _position
is probably an index into the raw array of bytes and they don't want to expose that. The String.Index
is meant to protect us from accessing bytes in the middle of characters.
That means that any index you get must be created from String.startIndex
or String.endIndex
(String.Index
implements BidirectionalIndexType
). Any other indices can be created using successor
or predecessor
methods.
Now to help us with indices, there is a set of methods (functions in Swift 1.x):
Swift 4.x
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.index(text.startIndex, offsetBy: 2)
let lastChar2 = text[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 3.0
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2)
let lastChar2 = text.characters[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 2.x
let text = "abc"
let index2 = text.startIndex.advancedBy(2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let lastChar2 = text.characters[index2] //will do the same as above
let range: Range<String.Index> = text.rangeOfString("b")!
let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match
Swift 1.x
let text = "abc"
let index2 = advance(text.startIndex, 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let range = text.rangeOfString("b")
let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times
Working with String.Index
is cumbersome but using a wrapper to index by integers (see https://stackoverflow.com/a/25152652/669586) is dangerous because it hides the inefficiency of real indexing.
Note that Swift indexing implementation has the problem that indices/ranges created for one string cannot be reliably used for a different string, for example:
Swift 2.x
let text: String = "abc"
let text2: String = "br>
let range = text.rangeOfString("b")!
//can randomly return a bad substring or throw an exception
let substring: String = text2[range]
//the correct solution
let intIndex: Int = text.startIndex.distanceTo(range.startIndex)
let startIndex2 = text2.startIndex.advancedBy(intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Swift 1.x
let text: String = "abc"
let text2: String = "br>
let range = text.rangeOfString("b")
//can randomly return nil or a bad substring
let substring: String = text2[range]
//the correct solution
let intIndex: Int = distance(text.startIndex, range.startIndex)
let startIndex2 = advance(text2.startIndex, intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Related Topics
Swiftyjson - Call Can Throw, But It Is Marked with 'Try' and the Error Is Not Handled
How to Handle Closure Recursivity
Count Elements of Array Matching Condition in Swift
How to Test If Objects Conforming to the Same Protocol Are Identical in Swift Without Casting
Parse.Com Querying User Class (Swift)
Why I Can Change/Reassigned a Constant Value That Instantiated from a Class
Unexpectedly Large Realm File Size
Why Should Not Directly Extend Uiview or Uiviewcontroller
Does Swift Have a Null Coalescing Operator and If Not, What Is an Example of a Custom Operator
Arkit - Viewport Size VS Real Screen Resolution
Swift Error Handling for Methods That Do Not Throw
Mutable Binding in Swiftui Live Preview
Error: Use of Unresolved Identifier 'Process'
Drag Rotate a Node Around a Fixed Point
Swift 4.2 Setter Getter, All Paths Through This Function Will Call Itself
Process Array in Parallel Using Gcd