Getting char from reference char of another String in Swift - swift

I want to get nTh char from target1 or target2 but using a reference char of abc String.
But result2 is not working as expected...
let abc = "abcde"
let target1 = "12345"
let target2 = "πŸ…πŸ…‘πŸ…’πŸ…“πŸ…”"
let m = abc.firstIndex(of: "c")!
let result1 = target1[m] //want "3" //working
let result2 = target2[m] //want "πŸ…’" //not working

String subscripting is a pretty bad idea, but if you insist on using it, you have to use distance and offset, not an absolute index. Like this:
let abc = "abcde"
let target1 = "12345"
let target2 = "πŸ…πŸ…‘πŸ…’πŸ…“πŸ…”"
let d = abc.distance(from: abc.startIndex, to: abc.firstIndex(of: "c")!)
let result1 = target1[target1.index(target1.startIndex, offsetBy:d)]
let result2 = target2[target2.index(target2.startIndex, offsetBy:d)]

Related

Replacing two ranges in a String simultaneously

Say you have a string that looks likes this:
let myStr = "Hello, this is a test String"
And you have two Ranges,
let rangeOne = myStr.range(of: "Hello") //lowerBound: 0, upperBound: 4
let rangeTwo = myStr.range(of: "this") //lowerBound: 7, upperBound: 10
Now you wish to replace those ranges of myStr with new characters, that may not be the same length as their original, you end up with this:
var myStr = "Hello, this is a test String"
let rangeOne = myStr.range(of: "Hello")!
let rangeTwo = myStr.range(of: "this")!
myStr.replaceSubrange(rangeOne, with: "Bonjour") //Bonjour, this is a test String
myStr.replaceSubrange(rangeTwo, with: "ce") //Bonjourceis is a test String
Because rangeTwo is based on the pre-altered String, it fails to properly replace it.
I could store the length of the replacement and use it to reconstruct a new range, but there is no guarantee that rangeOne will be the first to be replaced, nor that rangeOne will actually be first in the string.
The solution is the same as removing multiple items from an array by index in a loop.
Do it backwards
First replace rangeTwo then rangeOne
myStr.replaceSubrange(rangeTwo, with: "ce")
myStr.replaceSubrange(rangeOne, with: "Bonjour")
An alternative could be also replacingOccurrences(of:with:)
This problem can be solved by shifting the second range based on the length of first the replaced string.
Using your code, here is how you would do it:
var myStr = "Hello, this is a test String"
let rangeOne = myStr.range(of: "Hello")!
let rangeTwo = myStr.range(of: "this")!
let shift = "Bonjour".count - "Hello".count
let shiftedTwo = myStr.index(rangeTwo.lowerBound, offsetBy: shift)..<myStr.index(rangeTwo.upperBound, offsetBy: shift)
myStr.replaceSubrange(rangeOne, with: "Bonjour") // Bonjour, this is a test String
myStr.replaceSubrange(shiftedTwo, with: "ce") // Bonjour, ce is a test String
You can sort the range in descending order, then replace backwards, from the end to the start. So that any subsequent replacement will not be affect by the previous replacements. Also, it is safer to use replacingCharacters instead of replaceSubrange in case when dealing with multi-codepoints characters.
let myStr = "Hello, this is a test String"
var ranges = [myStr.range(of: "Hello")!,myStr.range(of: "this")!]
ranges.shuffle()
ranges.sort(by: {$1.lowerBound < $0.lowerBound}) //Sort in reverse order
let newWords : [String] = ["BonjourπŸ˜€","ce"].reversed()
var newStr = myStr
for i in 0..<ranges.count
{
let range = ranges[i]
//check overlap
if(ranges.contains(where: {$0.overlaps(range)}))
{
//Some range over lap
throw ...
}
let newWord = newWords[i]
newStr = newStr.replacingCharacters(in: range, with: newWord)
}
print(newStr)
My solution ended up being to take the ranges and replacement strings, work backwards and replace
extension String {
func replacingRanges(_ ranges: [NSRange], with insertions: [String]) -> String {
var copy = self
copy.replaceRanges(ranges, with: insertions)
return copy
}
mutating func replaceRanges(_ ranges: [NSRange], with insertions: [String]) {
var pairs = Array(zip(ranges, insertions))
pairs.sort(by: { $0.0.upperBound > $1.0.upperBound })
for (range, replacementText) in pairs {
guard let textRange = Range(range, in: self) else { continue }
replaceSubrange(textRange, with: replacementText)
}
}
}
Which works out to be useable like this
var myStr = "Hello, this is a test."
let rangeOne = NSRange(location: 0, length: 5) // β€œHello”
let rangeTwo = NSRange(location: 7, length: 4) // β€œthis”
myStr.replaceRanges([rangeOne, rangeTwo], with: ["Bonjour", "ce"])
print(myStr) // Bonjour, ce is a test.

Display of special characters \u{n}

Impossible to find the solution ; it does not work...
I've been on this for hours... A little help will give me the opportunity to sleep without a nightmare...
Where is the error ?
let num: Int = 128150 // smiley = "\u{1F496}" => 128150
var str: String = String(num, radix: 16)
str = str.uppercased()
var wkHex: String = "\\u{"+str+"}" // wkHex = "\u{"+str+"}" not match
wkHex.characters.removeFirst(0) // remove "\" unnecessary at startIndex
let cnt = wkHex.characters.count
let zzz: Array = Array(wkHex.characters)
var car: String = ""
for i in 0...cnt - 1 {
car.append(zzz[i])
}
outputChar.stringValue = car // outputChar is a Label (NSTextField)
// output : \u{1F496} ! instead of : πŸ’–
So the idea is to go from a code point to a character?
let iii = 128150
let char = Character(UnicodeScalar(iii)!)
print(char) // πŸ’–
Swift only allows you to use the \u{...} syntax at compile time. This means that the string won't be turned into the emoji at runtime, when the value of num is known.
To do this, you can use UnicodeScalar:
let unicode = UnicodeScalar(128150)
unicode?.description // πŸ’–

How to filter non-digits from string

My phone numbers are like +7 (777) 777-7777.
I need only digits and plus symbol: +77777777777 to make calls.
This returns only digits:
let stringArray = origString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let newString = NSArray(array: stringArray).componentsJoinedByString("")
One of the many ways to do that:
let isValidCharacter: (Character) -> Bool = {
($0 >= "0" && $0 <= "9") || $0 == "+"
}
let newString = String(origString.characters.filter(isValidCharacter))
or using a regular expression:
// not a +, not a number
let pattern = "[^+0-9]"
// replace anything that is not a + and not a number with an empty string
let newString = origString.replacingOccurrences(
of: pattern,
with: "",
options: .regularExpression
)
or, if you really want to use your original solution with a character set.
let validCharacters = CharacterSet(charactersIn: "0123456789+")
let newString = origString
.components(separatedBy: validCharacters.inverted)
.joined()
In keeping with the spirit of your partial solution,
let origString:NSString = "+7 (777) 777-7777"
let cs = NSCharacterSet(charactersIn: "0123456789+")
let final = origString.components(separatedBy: cs.inverted).joined()

How to succinctly get the first 5 characters of a string in swift?

What is the most succinct way to get the first 5 characters of a String in swift? Thank you.
First 5 chars
let str = "SampleText"
let result = String(str.characters.prefix(5)) // result = "Sampl"
SWIFT 4
let str = "SampleText"
let result = String(str.prefix(5)) // result = "Sampl"
In Swift 4 it changed:
let text = "sampleText"
let resultPrefix = text.prefix(5) //result = "sampl"
let resultSuffix = text.suffix(6) // result = "eText"
Get the index upto 5 character and then use substring
let str = "Hello World!"
if str.utf16.count >= 5{
let a = str.index(str.startIndex, offsetBy: 5)
let result = str.substring(to: a)
}else{
//lenght in shorter
}
In Swift 5,
let str:String = "Sam Fisher"
let result = str.prefix(3) //You get 'sam'

How I can take the number after the dot via substring?

I'm getting a value like this 264.8 and I want to take the value before and after the dot. I can take the value before the dot like this
var string = "264.8"
var index = string2.rangeOfString(".", options: .BackwardsSearch)?.startIndex
var substring = string.substringToIndex(index2!)
but please how I can take it after the dot?
Try this code:
var string = "264.8"
var numbers = string.componentsSeparatedByString(".")
print(numbers[0])
print(numbers[1])
var string = "264.8"
let partsArr = string.componentsSeparatedByString(".")
var beforeDot: String = partsArr[0]
var afterDot: String? = partsArr[1]
Just for the sake of completeness, an alternative is to use split:
let string = "264.8"
let result = string.characters.split(".").map { String($0) }
print(result[0]) // "264"
print(result[1]) // "8"
And another one is to use componentsSeparatedByCharactersInSet:
let string = "264.8"
let result = string.componentsSeparatedByCharactersInSet(NSCharacterSet.punctuationCharacterSet())
print(result[0]) // "264"
print(result[1]) // "8"
Alternatively, define a closure variable that handles the conversion for you
let mySubstringClosure : (String) -> (String) = { $0.componentsSeparatedByString(".").first ?? $0 }
let substring1 = mySubstringClosure("264.8") // "264"
let substring2 = mySubstringClosure("264") // "264"
let substring3 = mySubstringClosure("") // ""
Note that this code runs safely even if no dot . exists in the string, or of the string is empty.