How to find string B missing characters based on string A and add them to string B? - swift

I have 2 strings.
var stringA = "LetDoItNow"
var stringB = "LetDo"
Is there a way that I can find characters that are not in string B based on string A and add them to string B in order that present in String A? If they are equal, no action needed.
stringA always contains all stringB characters in exact order of stringA.
Another example is
var stringA = “GoodMorning”
var stringB = “GoodM”
stringB will always follow stringA order but it could be missing some stringA characters. String B will never be something like this
var stringB = “Morning”
The missing characters in the 1st case are "ItNow" and my expected result after checking is
stringB = "LetDoItNow"
I hope to get missing characters so that i can run this code
stringB += “missingCharacters”
Thank you so much!

you can use stringA.components(separatedBy
var stringA = "LetDoItNow"
var stringB = "LetDo"
if stringA.count > stringB.count {
let component = stringA.components(separatedBy: stringB)
if component.count > 1{
stringB = stringB+component[1]
}
}
print(stringB)
You can Configure it better than this to make generic solution

Use a dictionary - store all characters and occurrences in String B, then compare String A and add what you are missing.
func letdoitnow() {
var dict = [Character:Int]()
for each in stringB {
dict[each] = (dict[each] ?? 0) + 1
}
for each in stringA {
dict[each] = (dict[each] ?? 0) - 1
if dict[each]! < 0 {
b += String(each)
}
}
}
This does not care about order though - unsure if that matters to you

If you want to add missing characters that are in stringA but not in stringB to StringB then you can use the following function:
private func addMissingCharacters(stringA: String, stringB: String) -> String {
if stringA == stringB {
return stringA
}
var result = stringB
for charachter in stringA {
if !stringB.contains(charachter) {
result.append(charachter)
}
}
return result
}
Here is a usage example:
let stringA = "LetDoItNow"
let stringB = "LetDo"
let result = addMissingCharacters(stringA: stringA, stringB: stringB)
print("Result: \(result)") // Will print: LetDoINw
As you can see, the result is "LetDoINw" not "LetDoItNow", because 't' of "It" and 'o' of "Now" are there in stringB so they won't be added again.

Related

Swift get decimal value for String characters

Is it possible to get the decimal value for String characters in Swift?
something like:
let words:String = "1 ring to rule them all"
var value:Int = 0
for i in 0..<words.count {
let char = words[words.index(words.startIndex,offsetBy:i)]
value += Int(char.decimal)
}
where the first character in "1 ring to rule them all" is 49. Possible?
you could try this:
let words = "1 ring to rule them all"
var value: Int = 0
for i in 0..<words.count {
let char = words[words.index(words.startIndex,offsetBy:i)]
if let val = char.asciiValue {
print("----> char: \(char) val: \(val)") // char and its "decimal" value
value += Int(val)
}
}
print("\n----> value: \(value) \n") // meaningless total value
ok, looks like this is the way:
let characterString = "蜇"
let scalars = characterString.unicodeScalars
let ui32:UInt32 = scalars[scalars.startIndex].value
If you want to add up the Unicode values associated with a string, it would be:
var value = 0
for character in string {
for scalar in character.unicodeScalars {
value += Int(scalar.value)
}
}
Or, alternatively:
let value = string
.flatMap { $0.unicodeScalars }
.compactMap { $0.value }
.reduce(0, +)
While the above adds the values, as requested, if you are trying to get a numeric representation for a string, you might consider using hashValue, or checksum, or CRC, or the like. Simply summing the values will not be able to detect, for example, character transpositions. It just depends upon your use-case for this numeric representation of your string.

Efficient algorithm to split a string based on multiple string delimiters

I would like to know if there is an efficient way of splitting a string into multiple strings based on delimiters that are also strings.
Eg. updateStr = "23+45 = 56 0" , delimiters = ["+"," ","="]
Result = [23,45,56,0]
I tried the following code in swift:
for i in 0..<delimiter.count {
let res = updateStr.components(separatedBy: delimiter[i])
updateStr = res.joined(separator: "unique%")
}
splitTxt = updateStr.components(separatedBy: "unique%")
This works, but as the delimiters will be received dynamically I want a better approach.
Are there any efficient ways to avoid multiple loops to solve this?
An algorithm with more efficient solution that doesn't involve swift instance methods would also be appreciated.
Thanks for the answers but
To be clearer, I don't just want characters but strings as delimiters:
Eg2. updateStr = "I like playing with friends" , delimiters = ["li"," "la","ie"]
Result = ["I ","ke p","ying with fr","nds"]
The efficient way to do this sort of thing is with a Set:
let equation = "23+45 = 56 0"
let delimiters : [Character] = ["+"," ","="]
let setOfSeparators = Set(delimiters)
let result = equation.split {setOfSeparators.contains($0)}
print(result)
That's efficient because contains on a Set is extremely fast, so that cost is negligible and we are looping implicitly through the original string just once.
On the other hand, you could take advantage of the Cocoa CharacterSet class. For that, I would say:
let equation = "23+45 = 56 0"
let delimiters = ["+"," ","="]
let characterSet = CharacterSet(charactersIn: delimiters.joined())
let result = equation.components(separatedBy: characterSet).filter {!$0.isEmpty}
print(result)
Another fun way is to use a Scanner (these are underutilized in my opinion):
let equation = "23+45 = 56 0"
let delimiters = ["+"," ","="]
let characterSet = CharacterSet(charactersIn: delimiters.joined())
let scanner = Scanner(string: equation)
var result = [String]()
while let word = scanner.scanUpToCharacters(from: characterSet) {
result.append(word)
scanner.scanCharacters(from: characterSet)
}
print(result)
One of the components(separatedBy:) overloads will handle this automatically using a CharacterSet:
let delimiters = ["+"," ","="].compactMap(UnicodeScalar.init)
let splitTxt = updateStr.components(separatedBy: CharacterSet(delimiters))
NSRegularExpression provides the facility to split on general regular expressions, so this would enable splitting on a finite set of string delimiters using a delim1|delim2|delim3 regex. The following split operation does this job:
static func stringSubrange(str : String, st : Int, en : Int) -> String
{ var result : [Character] = [Character]()
var count : Int = 0
for index in str.indices
{ let c : Character = str[index]
count = count + 1
if count >= st && count <= en
{ result.append(c) }
else if count > en
{ return String(result) }
}
return String(result)
}
static func split(str: String, pattern: String) -> [String]
{ let rge = NSRange(location: 0, length: str.utf16.count)
let regexp = try! NSRegularExpression(pattern: pattern)
let pred = regexp.matches(in: str, options: [], range: rge)
var result : [String] = [String]()
var prev : Int = 1;
for p in pred
{ let range = p.range
let splitString = Ocl.stringSubrange(str: str, st: prev, en: range.location)
prev = range.location + range.length + 1
if splitString.count > 0
{ result.append(splitString) }
}
if prev < str.count
{ result.append(Ocl.stringSubrange(str: str, st: prev, en: str.count)) }
return result
}

Swift String to Int Always Returns nil

I am trying to convert a String to an Int. Seems simple enough, but for somer reason it is always returning nil.
I'm just writing a simple extension to convert dollars to cents:
func dollarsToCents() -> Int {
var temp = self;
temp = temp.replacingOccurrences(of: "$", with: "")
temp = temp.replacingOccurrences(of: ",", with: "")
if let number = Int(temp) {
return number*100
}
return 0
}
I have temp set to "$250.89". number is always nil. No matter how I approach converting temp to an Int it is always nil. Anyone know what I'm doing wrong?
Problem is, that string "250.89" (after removing currency symbol) can't be converted to Int because 250.89 isn't integer. So fix your code by converting it to Double
func dollarsToCents() -> Int {
var temp = self
temp.removeAll { "$,".contains($0) }
//temp = temp.replacingOccurrences(of: "[$,]", with: "", options: .regularExpression)
return Int(((Double(temp) ?? 0) * 100).rounded())
}
or if your "number" always have two decimal places
func dollarsToCents() -> Int {
var temp = self
temp.removeAll { !("0"..."9" ~= $0) }
return Int(temp) ?? 0
}
But I think solution is much easier. Your goal should be saving price value as number (Double,...). Then you don't have to convert String to Double and you can just multiply your number. Then when you need to add currency symbol, just convert your value to String and add $ or use NumberFormatter
let price = 250.89
let formattedPrice = "$\(price)" // $250.89
let price = 250.89
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyCode = "USD"
let formattedPrice = formatter.string(from: price as NSNumber)! // $250.89
Just adding this for the bizarre edge-case that I just encountered - be sure there aren't any whitespaces either trailing or leading in your string when converting to Int from String.
Eg.
for date in next_thirty_days {
let date_buffer: String = date.description[8...9]
date_list.append(Int(date_buffer)!)
}
This will fail if the value for date_buffer has a leading or trailing white-space.
Another edge-case is that there's a leading 0 in the string variable, eg. 07.
Hope it helps :)

How do I count the number of words in a String in Swift?

Say I have a String, how do I determine the number of words in it? I'm trying to create an extension like:
extension String {
var numberOfWords: Int {
// Insert string-counting code here
}
}
If you search "word count string swift" you'll find dozens of StackOverflow answers and gists that tell you to split the string using str.components(separatedBy: " ").count.
DON'T USE components(separatedBy:)!!!
Many non-European languages (particularly East Asian languages) don't use spaces to split words. This will also incorrectly count hyphenated words as separate, and lone punctuation as a word.
The most correct AND most performant way to solve this problem is to use either enumerateSubstrings(in:options:) or CFStringTokenizer.
// enumerateSubstrings
extension String {
var numberOfWords: Int {
var count = 0
let range = startIndex..<endIndex
enumerateSubstrings(in: range, options: [.byWords, .substringNotRequired, .localized], { _, _, _, _ -> () in
count += 1
})
return count
}
}
OR:
// CFStringTokenizer
extension String {
var numberOfWords: Int {
let inputRange = CFRangeMake(0, utf16.count)
let flag = UInt(kCFStringTokenizerUnitWord)
let locale = CFLocaleCopyCurrent()
let tokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, self as CFString, inputRange, flag, locale)
var tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)
var count = 0
while tokenType != [] {
count += 1
tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)
}
return count
}
}
Both are very performant, but enumerateSubtrings(in:options:...) is about twice as fast.
Shocked that nobody is pointing this out elsewhere, so I hope people searching for a solution find this.
Count of words in a string
Create an extension of String
extension String{
var wordCount:Int{
let chararacter = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters)
let comps = components(separatedBy: chararacter)
let words = comps.filter { !$0.isEmpty }
return words.count
}
}
How to use
"This is a test string".wordCount // Result: 5

How to mask a String to show only the last 3 characters?

I just tried to mask String as below, but I didn't find what I want after do some search and research.
string a = "0123456789"
masked = "xxxxxxx789"
I modified solutions in this questions http://stackoverflow.com/questions/41224637/masking-first-and-last-name-string-with but it just change the String that doesn't match with the pattern. I have no idea how to change the pattern to match with what I mean.
You can get the last 3 characters of your string using the collection method suffix(_ maxLength: Int) and fill the other part of the string repeating the "x":
edit/update
Swift 4 or later
extension StringProtocol {
var masked: String {
return String(repeating: "•", count: Swift.max(0, count-3)) + suffix(3)
}
}
let string = "0123456789"
print(string.masked) // "•••••••789\n"
This does exactly what you want:
let name = "0123456789"
let conditionIndex = name.characters.count - 3
let maskedName = String(name.characters.enumerated().map { (index, element) -> Character in
return index < conditionIndex ? "x" : element
})
print("Masked Name: ", maskedName) // xxxxxxx789
What happens here is that you get an array of the characters of the string using enumerated() method, then map each character to a value based on a condition:
If the index of the character is less than condtionIndex we replace the character with an x (the mask).
Else, we just leave the character as is.
who want this in obj C, can use this
NSString *phone = #"0123456789";
NSString *lastChr = [phone substringFromIndex: [phone length] - 3];
NSMutableString *mask = [[NSMutableString alloc]init];
for (int i=0; i<[phone length]-3; i++) {
[mask appendString:#"*"];
}
[mask appendString:lastChr];
I use this, very simple:
var pan: String?
var maskedPan: String { return pan.enumerated().map({ return ($0 < 6 || $0 > 11) ? String($1) : "*" }).joined()}
where $0 is index and $1 is iterated character