I want to learn the best/simplest way to turn a string into another string but with only a subset, starting at the beginning and going to the last index of a character.
String
has builtin substring feature:
extension String : Sliceable {
subscript (subRange: Range<String.Index>) -> String { get }
}
If what you want is "going to the first index of a character", you can get the substring using builtin find()
function:
var str = "www.stackexchange.com"
str[str.startIndex ..< find(str, ".")!] // -> "www"
To find last index, we can implement findLast()
.
/// Returns the last index where `value` appears in `domain` or `nil` if
/// `value` is not found.
///
/// Complexity: O(\ `countElements(domain)`\ )
func findLast<C: CollectionType where C.Generator.Element: Equatable>(domain: C, value: C.Generator.Element) -> C.Index? {
var last:C.Index? = nil
for i in domain.startIndex..<domain.endIndex {
if domain[i] == value {
last = i
}
}
return last
}
let str = "www.stackexchange.com"
let substring = map(findLast(str, ".")) { str[str.startIndex ..< $0] } // as String?
// if "." is found, substring has some, otherwise `nil`
ADDED:
Maybe, BidirectionalIndexType
specialized version of findLast
is faster:
func findLast<C: CollectionType where C.Generator.Element: Equatable, C.Index: BidirectionalIndexType>(domain: C, value: C.Generator.Element) -> C.Index? {
for i in lazy(domain.startIndex ..< domain.endIndex).reverse() {
if domain[i] == value {
return i
}
}
return nil
}
The one thing that adds clatter is the repeated stringVar
:
stringVar[stringVar.index(stringVar.startIndex, offsetBy: ...)
In Swift 4
An extension can reduce some of that:
extension String {
func index(at location: Int) -> String.Index {
return self.index(self.startIndex, offsetBy: location)
}
}
Then, usage:
let string = "abcde"
let to = string[..<string.index(at: 3)] // abc
let from = string[string.index(at: 3)...] // de
It should be noted that to
and from
are type Substring
(or String.SubSequance
). They do not allocate new strings and are more efficient for processing.
To get back a String
type, Substring
needs to be casted back to String
:
let backToString = String(from)
This is where a string is finally allocated.
I also build a simple String-extension for Swift 4:
extension String {
func subStr(s: Int, l: Int) -> String { //s=start, l=lenth
let r = Range(NSRange(location: s, length: l))!
let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
let toIndex = self.index(self.startIndex, offsetBy: r.upperBound)
let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
return String(self[indexRange])
}
}
So you can easily call it like this:
"Hallo world".subStr(s: 1, l: 3) //prints --> "all"
Try this Int-based
workaround:
extension String {
// start and end is included
func intBasedSubstring(_ start: Int, _ end: Int) -> String {
let endOffset: Int = -(count - end - 1)
let startIdx = self.index(startIndex, offsetBy: start)
let endIdx = self.index(endIndex, offsetBy: endOffset)
return String(self[startIdx..<endIdx])
}
}
Note: It's just a practice. It doesn't check the boundary. Modify to suit your needs.