Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save wuftymerguftyguff/b9c45c573d4699e91f7cb4ceb5e85576 to your computer and use it in GitHub Desktop.
Save wuftymerguftyguff/b9c45c573d4699e91f7cb4ceb5e85576 to your computer and use it in GitHub Desktop.
Added and amended to use pure Swift where possible
import Foundation
extension String
{
// Works in Xcode but not Playgrounds because of a bug with .insert()
mutating func insertString(string:String,ind:Int) {
var insertIndex = advance(self.startIndex, ind, self.endIndex)
for c in string {
self.insert(c, atIndex: insertIndex)
insertIndex = advance(insertIndex, 1)
}
}
// new replace method using replaceRange (replaces all instances of string)
mutating func replace(string:String, replacement:String) {
let ranges = self.rangesOfString(string)
// if the string isn't found return unchanged string
for r in ranges {
self.replaceRange(r, with: replacement)
}
}
// Swift pure containsString
func containsString(findStr:String) -> Bool {
var arr = [Range<String.Index>]()
var startInd = self.startIndex
var i = 0
// test first of all whether the string is likely to appear at all
if contains(self, first(findStr)!) {
startInd = find(self,first(findStr)!)!
}
else {
return false
}
// set starting point for search based on the finding of the first character
i = distance(self.startIndex, startInd)
while i<=countElements(self)-countElements(findStr) {
if self[advance(self.startIndex, i)..<advance(self.startIndex, i+countElements(findStr))] == findStr {
arr.append(Range(start:advance(self.startIndex, i),end:advance(self.startIndex, i+countElements(findStr))))
return true
}
i++
}
return false
}
// pure Swift - removeAtIndex no longer required, since the addition of following methods to String type
// mutating func removeAtIndex(i: String.Index) -> Character
// mutating func removeRange(subRange: Range<String.Index>)
// mutating func removeAll(keepCapacity: Bool = default)
// insert() method written in pure Swift, overloads the new String type method of the same name
func insert(string:String,ind:Int) -> String {
var insertIndex = advance(self.startIndex, ind, self.endIndex)
var returnString = toString(self)
for c in string {
returnString.insert(c, atIndex: insertIndex)
insertIndex = advance(insertIndex, 1)
}
return returnString
}
// rangesOfString: written in pure Swift (no Cocoa)
func rangesOfString(findStr:String) -> [Range<String.Index>] {
var arr = [Range<String.Index>]()
var startInd = self.startIndex
// check first that the first character of search string exists
if contains(self, first(findStr)!) {
// if so set this as the place to start searching
startInd = find(self,first(findStr)!)!
}
else {
// if not return empty array
return arr
}
var i = distance(self.startIndex, startInd)
while i<=countElements(self)-countElements(findStr) {
if self[advance(self.startIndex, i)..<advance(self.startIndex, i+countElements(findStr))] == findStr {
arr.append(Range(start:advance(self.startIndex, i),end:advance(self.startIndex, i+countElements(findStr))))
i = i+countElements(findStr)-1
// check again for first occurrence of character (this reduces number of times loop will run
if contains(self[advance(self.startIndex, i)..<self.endIndex], first(findStr)!) {
// if so set this as the place to start searching
i = distance(self.startIndex,find(self[advance(self.startIndex, i)..<self.endIndex],first(findStr)!)!) + i
countElements(findStr)
}
else {
return arr
}
}
i++
}
return arr
}
func stringByReplacingOccurrencesOfString(string:String, replacement:String) -> String {
// get ranges first using rangesOfString: method, then glue together the string using ranges of existing string and old string
let ranges = self.rangesOfString(string)
// if the string isn't found return unchanged string
if ranges.isEmpty {
return self
}
// using toString to make a copy so that self isn't altered
var newString = toString(self)
for r in ranges {
newString.replaceRange(r, with: replacement)
}
return newString
}
// Added String splitting methods that return arrays (Pure Swift)
func splitStringByCharacters() -> [Character] {
return map(self){return $0}
}
func splitStringByLines() -> [String] {
return split(self, {contains("\u{2028}\n\r", $0)
}, allowEmptySlices: false)
}
func splitStringByWords() -> [String] {
return split(self, {contains(" .,!:;()[]{}<>?\"'\u{2028}\u{2029}\n\r", $0)}, allowEmptySlices: false)
}
func splitStringByParagraphs() -> [String] {
return split(self, {contains("\u{2029}\n\r", $0)
}, allowEmptySlices: false)
}
func splitStringBySentences() -> [String] {
let arr:[Character] = ["\u{2026}",".","?", "!"]
var startInd = self.startIndex
var strArr = [String]()
for b in enumerate(self) {
for a in arr {
if a == b.element {
var endInd = advance(self.startIndex,b.index,self.endIndex)
//TODO: add method to allow for multiple punctuation at end of sentence, e.g. ??? or !!!
var str = self[startInd...endInd]
// removes initial spaces and returns from sentence
if contains(" \u{2028}\u{2029}\n\r",first(str)!) {
str = dropFirst(str)
}
strArr.append(str)
startInd = advance(endInd,1,self.endIndex)
}
}
}
return strArr
}
func regexMatchesInString(regexString:String) -> [String] {
var arr = [String]()
var rang = startIndex..<endIndex
var foundRange:Range<String.Index>?
repeat
{
foundRange = rangeOfString(regexString, options: NSStringCompareOptions.RegularExpressionSearch, range: rang, locale: nil)
if let fR = foundRange {
arr.append(substringWithRange(fR))
rang.startIndex = fR.endIndex
}
}
while foundRange != nil
return arr
}
var length: Int {
get {
return countElements(self)
}
}
subscript (i: Int) -> Character
{
get {
let index = advance(startIndex, i)
return self[index]
}
}
subscript (r: Range<Int>) -> String
{
get {
let startIndex = advance(self.startIndex, r.startIndex)
let endIndex = advance(self.startIndex, r.endIndex - 1)
return self[Range(start: startIndex, end: endIndex)]
}
}
func subString(startIndex: Int, length: Int) -> String
{
var start = advance(self.startIndex, startIndex)
var end = advance(self.startIndex, startIndex + length)
return self.substringWithRange(Range<String.Index>(start: start, end: end))
}
func indexOf(target: String) -> Int
{
var range = self.rangeOfString(target)
if let range = range {
return distance(self.startIndex, range.startIndex)
} else {
return -1
}
}
func indexOf(target: String, startIndex: Int) -> Int
{
var startRange = advance(self.startIndex, startIndex)
var range = self.rangeOfString(target, options: NSStringCompareOptions.LiteralSearch, range: Range<String.Index>(start: startRange, end: self.endIndex))
if let range = range {
return distance(self.startIndex, range.startIndex)
} else {
return -1
}
}
func lastIndexOf(target: String) -> Int
{
var index = -1
var stepIndex = self.indexOf(target)
while stepIndex > -1
{
index = stepIndex
if stepIndex + target.length < self.length {
stepIndex = indexOf(target, startIndex: stepIndex + target.length)
} else {
stepIndex = -1
}
}
return index
}
// Updated isMatch to remove reliance on NSRegularExpression
func isMatch(regex: String, options: NSStringCompareOptions?) -> Bool
{
let match = self.rangeOfString(regex, options: options ?? nil, range: Range(start: self.startIndex, end: self.endIndex), locale: nil)
return match != nil ? true : false
}
// getMatches updated to remove reliance on NSRegularExpression
func getMatches(str: String, options: NSStringCompareOptions?) -> [Range<String.Index>] {
var arr = [Range<String.Index>]()
var rang = startIndex..<endIndex
var foundRange:Range<String.Index>?
repeat
{
foundRange = rangeOfString(str, options: options ?? [], range: rang, locale: nil)
if let fR = foundRange {
arr.append(fR)
rang.startIndex = fR.endIndex
}
}
while foundRange != nil
return arr
}
private var vowels: [String]
{
return ["a", "e", "i", "o", "u"]
}
private var consonants: [String]
{
return ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"]
}
func pluralize(count: Int) -> String
{
if count == 1 {
return self
} else {
var lastChar = self.subString(self.length - 1, length: 1)
var secondToLastChar = self.subString(self.length - 2, length: 1)
var prefix = "", suffix = ""
if lastChar.lowercaseString == "y" && vowels.filter({x in x == secondToLastChar}).count == 0 {
prefix = self[0...self.length - 1]
suffix = "ies"
} else if lastChar.lowercaseString == "s" || (lastChar.lowercaseString == "o" && consonants.filter({x in x == secondToLastChar}).count > 0) {
prefix = self[0...self.length]
suffix = "es"
} else {
prefix = self[0...self.length]
suffix = "s"
}
return prefix + (lastChar != lastChar.uppercaseString ? suffix : suffix.uppercaseString)
}
}
// for fun, not sure whether this has a practical application
func removeDuplicates(array:String) -> String {
var arr = array
var indArr = [Int]()
var tempArr = arr
var i = 0
for a in arr {
if contains(prefix(arr, i), a) {
indArr.append(i)
}
i++
}
var ind = 0
for i in indArr {
arr.removeAtIndex(i-ind)
ind++
}
return arr
}
}
// sketchyTech added and updated begins
"Hello".regexMatchesInString("[^Hh]{1,}")
"Hello".isMatch("[x-z]{1,}", options: NSStringCompareOptions.RegularExpressionSearch)
"Hello".getMatches("[x-z]{1,}", options: NSStringCompareOptions.RegularExpressionSearch)
// pure Swift methods
"Hello".splitStringByCharacters()
"Hello. This is a new method.".splitStringByWords()
"Hello. This is a new method.".splitStringBySentences()
"Hello. This is a new method.\n I hope you enjoy using it.".splitStringByParagraphs()
"a very good hello, hello".rangesOfString("hello")
"a very good hello, hello".stringByReplacingOccurrencesOfString("hello",replacement:"bye")
"Hello World!".insert("Swift ", ind: 6)
// update replace method to use new replaceRange method
"a very good hello, hello".replace("hello",replacement:"bye")
// for fun (also pure Swift)
let str = "hello world"
str.removeDuplicates()
// insertString()
var a = "hello world"
a.insertString("big ", ind:6)
REMOVED: removeAtIndex
// pure Swift - removeAtIndex no longer required, since the addition of following methods to String type
// mutating func removeAtIndex(i: String.Index) -> Character
// mutating func removeRange(subRange: Range<String.Index>)
// mutating func removeAll(keepCapacity: Bool = default)
REMOVED: contains() and replaced with containsString()
str.containsString("play")
// sketchyTech added and updated ends
"ReplaceMe".replace("Me", withString: "You") == "ReplaceYou"
"MeReplace".replace("Me", withString: "You") == "YouReplace"
"ReplaceMeNow".replace("Me", withString: "You") == "ReplaceYouNow"
"0123456789"[0] == "0"
"0123456789"[5] == "5"
"0123456789"[9] == "9"
"0123456789"[5...6] == "5"
"0123456789"[0...1] == "0"
"0123456789"[8...9] == "8"
"0123456789"[1...5] == "1234"
"Reply"[0...4] == "Repl"
"Hello, playground"[0...5] == "Hello"
"Coolness"[4...7] == "nes"
"Awesome".indexOf("nothin") == -1
"Awesome".indexOf("Awe") == 0
"Awesome".indexOf("some") == 3
"Awesome".indexOf("e", startIndex: 3) == 6
"Awesome".lastIndexOf("e") == 6
"Cool".lastIndexOf("o") == 2
var emailRegex = "[a-z_\\-\\.]+@[a-z_\\-\\.]{3,}"
"[email protected]".isMatch(emailRegex, options: NSStringCompareOptions.CaseInsensitiveSearch) == true
"email-test.com".isMatch(emailRegex, options: NSStringCompareOptions.InsensitiveSearch) == false
var testText = "[email protected], [email protected], [email protected]"
var matches = testText.getMatches(emailRegex, options: NSStringCompareOptions.InsensitiveSearch)
matches.count == 3
testText.subString(matches[0].range.location, length: matches[0].range.length) == "[email protected]"
testText.subString(matches[1].range.location, length: matches[1].range.length) == "[email protected]"
testText.subString(matches[2].range.location, length: matches[2].range.length) == "[email protected]"
"Reply".pluralize(0) == "Replies"
"Reply".pluralize(1) == "Reply"
"Reply".pluralize(2) == "Replies"
"REPLY".pluralize(3) == "REPLIES"
"Horse".pluralize(2) == "Horses"
"Boy".pluralize(2) == "Boys"
"Cut".pluralize(2) == "Cuts"
"Boss".pluralize(2) == "Bosses"
"Domino".pluralize(2) == "Dominoes"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment