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

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

Related

Filter specific values from array of string

I have an array of following values
["130896-220","130897-901","130011-990","130012-991"]
and I am filter out of only those values which contain 900 to 999 at end of value.
I need following result of array ["130897-901","130011-990","130012-991"].
Simply call filter(_:) on arr with the relevant condition to filter the elements within the range 900...999
let arr = ["130896-220","130897-901","130011-990","130012-991"]
let range = (900...999)
let result = arr.filter({
if let str = $0.components(separatedBy: "-").last, let num = Int(str) {
return range.contains(num)
}
return false
})
print(result) //["130897-901", "130011-990", "130012-991"]
An alternative is Regular Expression
let array = ["130896-220","130897-901","130011-990","130012-991"]
let filteredArray = array.filter{$0.range(of: "^\\d{6}-9\\d{2}$", options: .regularExpression) != nil}
The pattern "^\\d{6}-9\\d{2}$" searches for
six digits followed by
one hyphen followed by
9 followed by
two other digits
A little bit unconventional Objective-C solution:
NSArray *data = #[#"130896-220", #"130896-901", #"130896-903"];
// We'll fill this array with the final filtered result
NSMutableArray* filteredData = [[NSMutableArray alloc] init];
for (NSString* str in data) { // Iterate between strings
int value = [[str componentsSeparatedByString:#"-"][1] intValue];
if(value > 900 && value < 1000){
[filteredData addObject:str];
}
}
Edit:
Inside the for I use the method componentsSeparatedByString:#"" this will return an array with strings separated by the entered value. In this case we know that we want the value after the "-" character so we access the second index of the array directly ([1]).
Lastly, we check if the value is compressed between the values we want. If so, is added to the filtered array.

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

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.

Masking email and phone number in Swift 4

What is the best way to mask / obfuscate email and phone number in Swift 4?
E.g. turning:
satheesmk2#gmail.com into sa******k2#gmail.com
9876543212 into 98*******2
extension String {
var maskEmail: String {
let email = self
let components = email.components(separatedBy: "#")
var maskEmail = ""
if let first = components.first {
maskEmail = String(first.enumerated().map { index, char in
return [0, 1, first.count - 1, first.count - 2].contains(index) ?
char : "*"
})
}
if let last = components.last {
maskEmail = maskEmail + "#" + last
}
return maskEmail
}
var maskPhoneNumber: String {
return String(self.enumerated().map { index, char in
return [0, 3, self.count - 1, self.count - 2].contains(index) ?
char : "*"
})
}
}
Basically, you could use a function like this. It hides all chars despite the leading and trailing two chars:
func hideMidChars(_ value: String) -> String {
return String(value.enumerated().map { index, char in
return [0, 1, value.count - 1, value.count - 2].contains(index) ? char : "*"
})
}
Please note there's currently no special handling done for very short input strings.
Using this function for the phone number should be trivial; as for the email, if you only want to hide the first part, you could use the following code:
let email = "123456#test.com"
let components = email.components(separatedBy: "#")
let result = hideMidChars(components.first!) + "#" + components.last!
The result constant will be 12**56#test.com.
This of course assumes that the email is in a valid format, otherwise force unwrapping the first and last array component could crash. However, handling this would be out of scope for this question.

Finding The First Non-repeating Character algorithm Swift 4 (Looping over string only once)

I am trying to solve code fights interview practice questions, but I am stuck on how to solve this particular problem in swift. My first thought was to use a dictionary with the counts of each character, but then I would have to iterate over the string again to compare, so that doesn't work per the restrictions. Any help would be good. Thank you. Here is the problem and requirements:
Note: Write a solution that only iterates over the string once and uses O(1) additional memory, since this is what you would be asked to do during a real interview.
Given a string s, find and return the first instance of a non-repeating character in it. If there is no such character, return '_'
Here is the code I started with (borrowed from another post)
func firstNotRepeatingCharacter(s: String) -> Character {
var countHash:[Character:Int] = [:]
for character in s {
countHash[character] = (countHash[character] ?? 0) + 1
}
let nonRepeatingCharacters = s.filter({countHash[$0] == 1})
let firstNonRepeatingCharacter = nonRepeatingCharacters.first!
return firstNonRepeatingCharacter
}
firstNotRepeatingCharacter(s:"abacabad")
You can create a dictionary to store the occurrences and use first(where:) method to return the first occurrence that happens only once:
Swift 4
func firstNotRepeatingCharacter(s: String) -> Character {
var occurrences: [Character: Int] = [:]
s.forEach{ occurrences[$0, default: 0] += 1 }
return s.first{ occurrences[$0] == 1 } ?? "_"
}
Swift 3
func firstNotRepeatingCharacter(s: String) -> Character {
var occurrences: [Character:Int] = [:]
s.characters.forEach{ occurrences[$0] = (occurrences[$0] ?? 0) + 1}
return s.characters.first{ occurrences[$0] == 1 } ?? "_"
}
Another option iterating the string in reversed order and using an array of 26 elements to store the characters occurrences
func firstNotRepeatingCharacter(s: String) -> Character {
var chars = Array(repeating: 0, count: 26)
var characters: [Character] = []
var charIndex = 0
var strIndex = 0
s.characters.reversed().forEach {
let index = Int(String($0).unicodeScalars.first!.value) - 97
chars[index] += 1
if chars[index] == 1 && strIndex >= charIndex {
characters.append($0)
charIndex = strIndex
}
strIndex += 1
}
return characters.reversed().first { chars[Int(String($0).unicodeScalars.first!.value) - 97] == 1 } ?? "_"
}
Use a dictionary to store the character counts as well as where they were first encountered. Then, loop over the dictionary (which is constant in size since there are only so many unique characters in the input string, thus also takes constant time to iterate) and find the earliest occurring character with a count of 1.
func firstUniqueCharacter(in s: String) -> Character
{
var characters = [Character: (count: Int, firstIndex: Int)]()
for (i, c) in s.characters.enumerated()
{
if let t = characters[c]
{
characters[c] = (t.count + 1, t.firstIndex)
}
else
{
characters[c] = (1, i)
}
}
var firstUnique = (character: Character("_"), index: Int.max)
for (k, v) in characters
{
if v.count == 1 && v.firstIndex <= firstUnique.index
{
firstUnique = (k, v.firstIndex)
}
}
return firstUnique.character
}
Swift
Use dictionary, uniqueCharacter optional variable with unique characters array to store all uniquely present characters in the string , every time duplication of characters found should delete that character from unique characters array and same time it is the most first character then should update the dictionary with its count incremented , refer following snippet , how end of the iteration through all characters gives a FIRST NON REPEATED CHARACTER in given String. Refer following code to understand it properly
func findFirstNonRepeatingCharacter(string:String) -> Character?{
var uniqueChars:[Character] = []
var uniqueChar:Character?
var chars = string.lowercased().characters
var charWithCount:[Character:Int] = [:]
for char in chars{
if let count = charWithCount[char] { //amazon
charWithCount[char] = count+1
if char == uniqueChar{
uniqueChars.removeFirst()
uniqueChar = uniqueChars.first
}
}else{
charWithCount[char] = 1
uniqueChars.append(char)
if uniqueChar == nil{
uniqueChar = char
}
}
}
return uniqueChar
}
// Use
findFirstNonRepeatingCharacter(string: "eabcdee")

Swift: Cannot assign value of type 'String.CharacterView.index' to type 'Int'

Does anyone know why I get the following error when I run my code below?
Error: Cannot assign value of type 'String.CharacterView.index' to type 'Int'
var problem = "find the longest word in the problem description"
var last = problem.characters.last
let lastIndex = problem.characters.endIndex;
print(last);
var words:[String] = [String]();
var word:String = "";
var lastCheck:Int = 0;
for i in problem.characters{
lastCheck = lastCheck + 1
if i != " "{
word = word + String(i)
}
else if lastCheck = lastIndex{
words.append(word);
}
else if i == " "{
words.append(word)
word = ""
}
}
print(words)
UPDATE
I have tried changing the let lastIndex to var lastIndex but no luck
You can't compare lastCheck (an Int) and lastIndex (an Index).
You have to convert it:
var problem = "find the longest word in the problem description"
var last = problem.characters.last
let lastIndex = problem.characters.endIndex;
print(last);
var words:[String] = [String]();
var word:String = "";
var lastCheck:Int = 0;
for i in problem.characters{
lastCheck = lastCheck + 1
let lastIndexInt = problem.characters.startIndex.distanceTo(end: lastIndex) // new
if i != " "{
word = word + String(i)
}
else if lastCheck = lastIndex{
words.append(word);
}
else if i == " "{
words.append(word)
word = ""
}
}
print(words)
Here's a simpler solution to your problem:
let input = "find the longest word in the problem description";
let longest = input
.characters //get the input characters
.split(separator:" ", //split by spaces
maxSplits: 1000, //max number of splits
omittingEmptySubsequences: true) //omit "" splits
.map(String.init) //make new strings from the characters
.max{$0.characters.count < $1.characters.count} //get longest length word
print(longest)
Thanks to originaluser2
String.CharacterView.Index is a struct but not Int. So you cannot assign a Int to your String.CharacterView.Index variable. It's normal.
You have to convert to Int as #Amomchilov's answer, or use Index :
...
var lastCheck:String.CharacterView.Index = problem.startIndex
for i in problem.characters {
lastCheck = lastCheck.advancedBy(1)
...
}
Anyway, in order to find the longest word of a phrase, you can use builtIn function of Swift to get all words then compare their length.
For example:
let str = "find the longest word in the problem description"
var longestWord : String?
str.enumerateSubstringsInRange(str.startIndex..<str.endIndex, options:.ByWords) {
(substring, substringRange, enclosingRange, value) in
if let _subString = substring {
if longestWord == nil || longestWord!.characters.count < _subString.characters.count {
longestWord = _subString
}
}
}
Easy way to get all the words in sentence
var problem = "find the longest word in the problem description"
problem.enumerateSubstrings(in: (problem.range(of: problem))!, options: NSString.EnumerationOptions.byWords, { (substring, substringRange, enclosingRange, stop) -> () in
print(substring)
})
Easy way to get longest word in sentence
var longestWord = ""
problem.enumerateSubstrings(in: (problem.range(of: problem))!, options: NSString.EnumerationOptions.byWords, { (substring, substringRange, enclosingRange, stop) -> () in
if longestWord.characters.count < substring?.characters.count{
longestWord = substring!
}
})
print(longestWord)
Note:Code with reference to Swift 3.For lower version of there will be some syntax changes.Let me know if you need the same for lower versions.