Created
December 7, 2020 16:16
-
-
Save JacopoMangiavacchi/2d9bc69f39057a7758dfae121a881789 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
public extension String { | |
/** | |
Enables passing in negative indices to access characters | |
starting from the end and going backwards. | |
if num is negative, then it is added to the | |
length of the string to retrieve the true index. | |
*/ | |
func negativeIndex(_ num: Int) -> Int { | |
return num < 0 ? num + self.count : num | |
} | |
func strOpenRange(index i: Int) -> Range<String.Index> { | |
let j = negativeIndex(i) | |
return strOpenRange(j..<(j + 1), checkNegative: false) | |
} | |
func strOpenRange( | |
_ range: Range<Int>, checkNegative: Bool = true | |
) -> Range<String.Index> { | |
var lower = range.lowerBound | |
var upper = range.upperBound | |
if checkNegative { | |
lower = negativeIndex(lower) | |
upper = negativeIndex(upper) | |
} | |
let idx1 = index(self.startIndex, offsetBy: lower) | |
let idx2 = index(self.startIndex, offsetBy: upper) | |
return idx1..<idx2 | |
} | |
func strClosedRange( | |
_ range: CountableClosedRange<Int>, checkNegative: Bool = true | |
) -> ClosedRange<String.Index> { | |
var lower = range.lowerBound | |
var upper = range.upperBound | |
if checkNegative { | |
lower = negativeIndex(lower) | |
upper = negativeIndex(upper) | |
} | |
let start = self.index(self.startIndex, offsetBy: lower) | |
let end = self.index(start, offsetBy: upper - lower) | |
return start...end | |
} | |
// MARK: - Subscripts | |
/** | |
Gets and sets a character at a given index. | |
Negative indices are added to the length so that | |
characters can be accessed from the end backwards | |
Usage: `string[n]` | |
*/ | |
subscript(_ i: Int) -> String { | |
get { | |
return String(self[strOpenRange(index: i)]) | |
} | |
set { | |
let range = strOpenRange(index: i) | |
replaceSubrange(range, with: newValue) | |
} | |
} | |
/** | |
Gets and sets characters in an open range. | |
Supports negative indexing. | |
Usage: `string[n..<n]` | |
*/ | |
subscript(_ r: Range<Int>) -> String { | |
get { | |
return String(self[strOpenRange(r)]) | |
} | |
set { | |
replaceSubrange(strOpenRange(r), with: newValue) | |
} | |
} | |
/** | |
Gets and sets characters in a closed range. | |
Supports negative indexing | |
Usage: `string[n...n]` | |
*/ | |
subscript(_ r: CountableClosedRange<Int>) -> String { | |
get { | |
return String(self[strClosedRange(r)]) | |
} | |
set { | |
replaceSubrange(strClosedRange(r), with: newValue) | |
} | |
} | |
/// `string[n...]`. See PartialRangeFrom | |
subscript(r: PartialRangeFrom<Int>) -> String { | |
get { | |
return String(self[strOpenRange(r.lowerBound..<self.count)]) | |
} | |
set { | |
replaceSubrange(strOpenRange(r.lowerBound..<self.count), with: newValue) | |
} | |
} | |
/// `string[...n]`. See PartialRangeThrough | |
subscript(r: PartialRangeThrough<Int>) -> String { | |
get { | |
let upper = negativeIndex(r.upperBound) | |
return String(self[strClosedRange(0...upper, checkNegative: false)]) | |
} | |
set { | |
let upper = negativeIndex(r.upperBound) | |
replaceSubrange( | |
strClosedRange(0...upper, checkNegative: false), with: newValue | |
) | |
} | |
} | |
/// `string[...<n]`. See PartialRangeUpTo | |
subscript(r: PartialRangeUpTo<Int>) -> String { | |
get { | |
let upper = negativeIndex(r.upperBound) | |
return String(self[strOpenRange(0..<upper, checkNegative: false)]) | |
} | |
set { | |
let upper = negativeIndex(r.upperBound) | |
replaceSubrange( | |
strOpenRange(0..<upper, checkNegative: false), with: newValue | |
) | |
} | |
} | |
/// `string.index(of: string, from: 0)`. | |
func index(of str: String, from: Int = 0) -> Int? { | |
guard from >= 0 && from < self.count else { | |
return nil | |
} | |
for i in from..<self.count { | |
var pos = 0 | |
while pos < str.count && | |
i+pos < self.count && | |
self[i+pos] == str[pos] { | |
pos += 1 | |
} | |
if pos == str.count { | |
return i | |
} | |
} | |
return nil | |
} | |
/// `string.replace(old: string, new: string)`. | |
func replaced(old: String, new: String = "") -> String { | |
var output = "" | |
var pos=0 | |
while let index = self.index(of: old, from: pos) { | |
output += self[pos..<index] + new | |
pos = index + old.count | |
} | |
output += self[pos...] | |
return output | |
} | |
} | |
// TEST | |
// for test in ["ala", "ago", "go", "agogola", "aggo", "aggoola", "a😄g😇go🥸la"] { | |
// print(test.replaced(old: "go", new: "")) | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment