How to separate characters in String by whitespace with multiple strides? - swift

I have a working function that separates every n character with whitespace, which works fine.
Here is the code (Swift 5):
extension String {
/// Creates a new string, separating characters specified by stride lenght.
/// - Parameters:
/// - stride: Desired stride lenght.
/// - separator: Character to be placed in between separations
func separate(every stride: Int, with separator: Character) -> String {
return String(self.enumerated().map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1] }.joined())
}
}
This prints an example string of 1234123412341234 like this
1234 1234 1234 1234
Now, how can i separate this string 1234123412341234 with multiple strides, for example white space to be set after 4th, then after 6th and then after 5th character, like this:
1234 123412 34123 4

Here's how I would do this:
// Prints sequences of bools using 1/0s for easy reading
func p<S: Sequence>(_ bools: S) where S.Element == Bool {
print(bools.map { $0 ? "1" : "0"}.joined())
}
// E.g. makeWindow(span: 3) returns 0001
func makeWindow(span: Int) -> UnfoldSequence<Bool, Int> {
return sequence(state: span) { state in
state -= 1
switch state {
case -1: return nil
case 0: return true
case _: return false
}
}
}
// E.g. calculateSpacePositions(spans: [4, 6, 5]) returns 000100000100001
func calculateSpacePositions<S: Sequence>(spans: S)
-> LazySequence<FlattenSequence<LazyMapSequence<S, UnfoldSequence<Bool, Int>>>>
where S.Element == Int {
return spans.lazy.flatMap(makeWindow(span:))
}
extension String {
func insertingSpaces(at spans: [Int]) -> String {
let spacePositions = calculateSpacePositions(spans: spans + [Int.max])
// p(spacePositions.prefix(self.count))
let characters = zip(inputString, spacePositions)
.flatMap { character, shouldHaveSpace -> [Character] in
return shouldHaveSpace ? [character, "_"] : [character]
}
return String(characters)
}
}
let inputString = "1234123412341234"
let result = inputString.insertingSpaces(at: [4, 6, 5])
print(result)
The main idea is that I want to zip(self, spacePositions), so that I obtain a sequence of the characters of self, along with a boolean that tells me if I should append a space after the current character.
To calculate spacePositions, I first started by making a function that when given an Int input span, would return span falses followed by a true. E.g. makeWindow(span: 3) returns a sequence that yields false, false, false, true.
From there, it's just a matter of making one of these windows per element of the input, and joining them all together using flatMap. I do this all lazily, so that we don't actually need to store all of these repeated booleans.
I hit one snag though. If you give the input [4, 6, 5], the output I would get used to be 4 characters, space, 6 characters, space, 5 characters, end. The rest of the string was lost, because zip yields a sequence whose length is equal to the length of the shorter of the two inputs.
To remedy this, I append Int.max on the spans input. That way, the space positions are 000010000001000001 ...now followed by Int.max falses.

func separate(text: String,every stride: [Int], with separator: Character)->String {
var separatorLastPosition = 0 // This is the last separator position in text
var myText = text
if text.count < stride.reduce(0,+){
return text //if your text length not enough for adding separator for all stride positions it will return the text without modifications.you can return error msg also
}else{
for (index, item) in stride.enumerated(){
myText.insert(separator, at:myText.index(myText.startIndex, offsetBy: index == 0 ? item : separatorLastPosition+item))
separatorLastPosition += item+1
}
return myText
}
}
print(separate(text: "12345678901234567890", every: [2,4,5,2], with: " "))
//Result -- 12 3456 78901 23 4567890

func separateCharcters(numbers: String, every: inout [Int], character: Character) ->String{
var counter = 0
var numbersWithSpaces = ""
for (_, number) in numbers.enumerated(){
numbersWithSpaces.append(number)
if !every.isEmpty{
counter += 1
if counter == every.first!{
numbersWithSpaces.append(character)
every.removeFirst()
counter = 0
}
}
}
return numbersWithSpaces
}
Test Case
var numberArray = [4, 6, 5]
separateCharcters(numbers: "1234123412341234", every: &numberArray, character: " ")
Return Result = "1234 123412 34123 4"

Related

Get Length of a substring in string before certain character Swift

My main string is like this "90000+8000-1000*10". I wanted to find the length of substring that contain number and make it into array. So it will be like this:
print(substringLength[0]) //Show 5
print(substringLength[1]) //Show 4
Could anyone help me with this? Thanks in advance!
⚠️ Be aware of using replacingOccurrences!
Although this method (mentioned by #Raja Kishan) may work in some cases, it's not forward compatible and will fail if you have unhandled characters (like other expression operators)
βœ… Just write it as you say it:
let numbers = "90000+8000-1000*10".split { !$0.isWholeNumber && $0 != "." }
You have the numbers! go ahead and count the length
numbers[0].count // show 5
numbers[1].count // shows 4
🎁 You can also have the operators like:
let operators = "90000+8000-1000*10".split { $0.isWholeNumber || $0 == "." }
You can split when the character is not a number.
The 'max splits' method is used for performance, so you don't unnecessarily split part of the input you don't need. There are also preconditions to handle any bad input.
func substringLength(of input: String, at index: Int) -> Int {
precondition(index >= 0, "Index is negative")
let sections = input.split(maxSplits: index + 1, omittingEmptySubsequences: false) { char in
!char.isNumber
}
precondition(index < sections.count, "Out of range")
return sections[index].count
}
let str = "90000+8000-1000*10"
substringLength(of: str, at: 0) // 5
substringLength(of: str, at: 1) // 4
substringLength(of: str, at: 2) // 4
substringLength(of: str, at: 3) // 2
substringLength(of: str, at: 4) // Precondition failed: Out of range
If the sign (operator) is fixed then you can replace all signs with a common one sign and split the string by a common sign.
Here is the example
extension String {
func getSubStrings() -> [String] {
let commonSignStr = self.replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "*", with: "-")
return commonSignStr.components(separatedBy: "-")
}
}
let str = "90000+8000-1000*10"
str.getSubStrings().forEach({print($0.count)})
I'd assume that the separators are not numbers, regardless of what they are.
let str = "90000+8000-1000*10"
let arr = str.split { !$0.isNumber }
let substringLength = arr.map { $0.count }
print(substringLength) // [5, 4, 4, 2]
print(substringLength[0]) //Show 5
print(substringLength[1]) //Show 4
Don't use isNumber Character property. This would allow fraction characters as well as many others that are not single digits 0...9.
Discussion
For example, the following characters all represent numbers:
β€œ7” (U+0037 DIGIT SEVEN)
β€œβ…šβ€ (U+215A VULGAR FRACTION FIVE SIXTHS)
β€œγŠˆβ€ (U+3288 CIRCLED IDEOGRAPH NINE)
β€œπŸ β€ (U+1D7E0 MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT)
β€œΰΉ’β€ (U+0E52 THAI DIGIT TWO)
let numbers = "90000+8000-1000*10".split { !("0"..."9" ~= $0) } // ["90000", "8000", "1000", "10"]
let numbers2 = "90000+8000-1000*10 ΰ₯« ΰΉ™ δΈ‡ β…š 𝟠 ΰΉ’ ".split { !("0"..."9" ~= $0) } // ["90000", "8000", "1000", "10"]

How to go through the elements of type string

I am writing a function that should return the number of duplicates. I can’t understand how to go through the elements of type string.
func countDuplicates(_ s:String) -> Int {
var duplicates = 0
for i in s {
duplicates += 1
}
return duplicates
}
If you want to find the total sum of duplicates, the code below could be helpful:
let text = "HelloooE"
func numberOfDuplicates(_ s: String) -> Int { s.count - Set(s).count }
print(numberOfDuplicates(text)) //Answer: 3 >> which says one "l" and two "o"s are duplicated.
But if the count of duplicated characters is required, this should be the answer:
let text = "HelloooE"
func countOfDuplicateChars(_ s: String) -> Int {
s.reduce(into: [:]) {result, word in result[word, default: 0] += 1 }
.filter { $0.value > 1 }
.count
}
print(countOfDuplicateChars(text)) //Answer: 2 >> which says "l" and "o" are duplicated characters.
The function below is also return the count of duplicated characters and also is case insensitive:
let text = "HelloooE"
func countOfDuplicateChars_CS(_ s: String) -> Int {
s.lowercased()
.reduce(into: [:]) {result, word in result[word, default: 0] += 1 }
.filter { $0.value > 1 }
.count
}
print(countOfDuplicateChars(text)) //Answer: 3 >> which says "l", "o" and "e" are duplicated characters.
You can try:
func countDuplicates(_ s:String) -> Int {
return s.count - Set(s).count
}
Where:
s.count // total number of characters
Set(s).count // number of unique characters
You don't need to go through the string to find the count of the duplicates. you can just subtract the count and the not duplicated version called Set.
func countDuplicates(_ s: String) -> Int { s.count - Set(s).count) }
Note
You should consider maybe there will be more than one duplicated character there. So you need to find all duplicates for example by grouping similar and then count them:
let duplications = Dictionary(grouping: text, by: {$0}).map { [$0.key: $0.value.count] }
print(duplications)
You can get the sum of the duplicated ones if you want:
let totalDuplicationCount = dups.filter {$0.count > 1}.reduce(into: 0) { $0 += $1.count - 1 }
print(totalDuplicationCount)

Swift Binary Search Falling Through to last return statement

I am trying to implement binary search in Swift 4. The code seems to work, except that the code is falling through to the last return statement. I tried putting it in an else clause but get a compiler warning saying that control reaches end of non-void. I want it so if the conditions are met, that the code will return early and not exit with the -1 value of the last return statement.
let numbersArray:[Int] = [1, 2, 3, 4, 6, 6, 6, 7, 7, 8, 9, 11, 13, 15, 17, 19, 20]
var first: Int = 0
var last: Int = numbersArray.count - 1
func binarySearch(array: [Int], number: Int) -> Int{
if array.count == 0 {return -1}
else if array.count == 1{
if array[0] == number {return array[0]}
else {return -1}
}
let arrayMiddle: Int = array.count / 2
if number == array[arrayMiddle] {return array[arrayMiddle]}
else if number > array[arrayMiddle]{
first = arrayMiddle + 1
print("first in number > middle \(first)")
last = array.count - 1
print("last in number > middle \(last)")
let slice: [Int] = Array(array[first...last])
binarySearch(array: slice, number: number)
}
else if number < array[arrayMiddle]{
last = arrayMiddle - 1
print("last in number < middle \(last)")
first = 0
print("first in number < middle \(first)")
let slice: [Int] = Array(array[first...last])
binarySearch(array: slice, number: number)
}
print("got to last case")
return -1
}
You're recursively calling binarySearch without returning the result, twice.
You should use a switch and test against your 3 cases:
The value is after the middle. Create a slice after the middle and recursively call into that slice, returning the result of that call.
The value is the middle, return the middle index.
The value is before the middle. Create a slice before the middle and recursively call into that slice, returning the result of that call.
Also, I would make this generic and an extension on RandomAccessCollection. That way you don't need to turn each ArraySlice into an Array when you call the recursion. The other advantage is that ArraySlice maintains the indices of the original collection so you won’t need to maintain them yourself. By creating new Array instances you’re throwing that away.
Lastly, instead of returning a -1 you might want to use an Optional and return nil to indicate that the value is not in the collection. That's a fairly standard way to handle it.
My implementation:
extension RandomAccessCollection {
func binarySearch<T>(value: T) -> Self.Index? where
T: Comparable, Self.Element == T {
guard !self.isEmpty else { return nil }
let middle = self.index(self.startIndex, offsetBy: self.count / 2)
switch self[middle] {
case ..<value:
let next = self.index(after: middle)
return self[next...].binarySearch(value: value)
case value:
return middle
default:
return self[..<middle].binarySearch(value: value)
}
}
}

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")

Iterating based on a variable number of inner loops

In the below code I am trying to go through all possible combination of alphabets for number of characters which are runtime variable.
The purpose of this code is to build a kind of password cracker, which basically brute-force guess the string. I want to use loop, because I will be able to break the loop as soon as the correct combination is hit thus saving on time and resources which otherwise will be required if I try to build an array of all possible combinations in first step.
I have a static code which works for a string 5 characters long but in reality my string could be any length. How can I make my code work with any length of string?
let len = textField.text?.characters.count //Length of string
let charRange = "abcdefghijklmnopqrstuvwxyz" //Allowed characterset
for char1 in charRange.characters {
for char2 in charRange.characters {
for char3 in charRange.characters {
for char4 in charRange.characters {
for char5 in charRange.characters {
// Do whatever with all possible combinations
}
}
}
}
}
I think I have to utilize for totalChars in 1...len { somehow but can't figure out how the for loops are going to be created dynamically?
Idea: form the string using an array of indices into your alphabet; each time increment the indices.
[0, 0, 0] -> [1, 0, 0] -> [2, 0, 0] ->
[0, 1, 0] -> [1, 1, 0] -> [2, 1, 0] ->
[0, 2, 0] -> [1, 2, 0] -> [2, 2, 0] ->
[0, 0, 1] ... [2, 2, 2]
Here's an example using a length of 3 and an alphabet of abcd
let len = 3
let alphabet = "abcd".characters.map({ String($0) })
var allStrings = [String]()
let maxIndex = alphabet.endIndex
var indicies = Array(count: len, repeatedValue: 0)
outerLoop: while (true) {
// Generate string from indicies
var string = ""
for i in indicies {
let letter = alphabet[i]
string += letter
}
allStrings.append(string)
print("Adding \(string)")
// Increment the index
indicies[0] += 1
var idx = 0
// If idx overflows then (idx) = 0 and (idx + 1) += 1 and try next
while (indicies[idx] == maxIndex) {
// Reset current
indicies[idx] = 0
// Increment next (as long as we haven't hit the end done)
idx += 1
if (idx >= alphabet.endIndex - 1) {
print("Breaking outer loop")
break outerLoop
}
indicies[idx] += 1
}
}
print("All Strings: \(allStrings)")
As suggested by Martin R, you can use recursion
This is the function
func visit(alphabet:[Character], combination:[Character], inout combinations:[String], length: Int) {
guard length > 0 else {
combinations.append(String(combination))
return
}
alphabet.forEach {
visit(alphabet, combination: combination + [$0], combinations: &combinations, length: length - 1)
}
}
The helper function
func combinations(alphabet: String, length: Int) -> [String] {
var combinations = [String]()
visit([Character](alphabet.characters), combination: [Character](), combinations: &combinations, length: length)
return combinations
}
Test
Now if you want every combination of 3 chars, and you want "ab" as alphabet then
combinations("ab", length: 3) // ["aaa", "aab", "aba", "abb", "baa", "bab", "bba", "bbb"]
Duplicates
Please note that if you insert duplicates into your alphabet, you'll get duplicate elements into the result.
Time complexity
The visit function is invoked as many times as the nodes into a perfect k-ary tree with height h where:
k: the number of elements into the alphabet param
h: the length param
Such a tree has
nodes. And this is the exact number of times the function will be invoked.
Space complexity
Theoretically The max number of stack frames allocated at the same time to execute visit is length.
However since the Swift compiler does implement the Tail Call Optimization the number of allocated stack frames is only 1.
Finally we must consider that combinations will be as big as the number of results: alphabet^length
So the time complexity is the max of length and elements into the result.
And it is O(length + alphabet^length)
Update
It turns out you want a brute force password breaker so.
func find(alphabet:[Character], combination:[Character] = [Character](), length: Int, check: (keyword:String) -> Bool) -> String? {
guard length > 0 else {
let keyword = String(combination)
return check(keyword: keyword) ? keyword : nil
}
for char in alphabet {
if let keyword = find(alphabet, combination: combination + [char], length: length - 1, check: check) {
return keyword
}
}
return nil
}
The last param check is a closure to verify if the current word is the correct password. You will put your logic here and the find will stop as soon as the password is found.
Example
find([Character]("tabcdefghil".characters), length: 3) { (keyword) -> Bool in
return keyword == "cat" // write your code to verify the password here
}
Alternative to recursion; loop radix representation of incremental (repeated) traversing of your alphabet
An alternative to recursion is to loop over an numeral representation of your alphabet, using a radix representative for the different number of letters. A limitation with this method is that the String(_:,radix:) initializer allows at most base36 numbers (radix 36), i.e., you can at most perform your "password cracking" with a set of characters with a unique count <=36.
Help function
// help function to use to pad incremental alphabeth cycling to e.g. "aa..."
let padToTemplate: (str: String, withTemplate: String) -> String = {
return $0.characters.count < $1.characters.count
? String($1.characters.suffixFrom($0.characters.endIndex)) + $0
: $0
}
Main radix brute-force password checking method
// attempt brute-force attempts to crack isCorrectPassword closure
// for a given alphabet, suspected word length and for a maximum number of
// attempts, optionally with a set starting point
func bruteForce(isCorrectPassword: (String) -> Bool, forAlphabet alphabet: [Character], forWordLength wordLength: Int, forNumberOfAttempts numAttempts: Int, startingFrom start: Int = 0) -> (Int, String?) {
// remove duplicate characters (but preserve order)
var exists: [Character:Bool] = [:]
let uniqueAlphabet = Array(alphabet.filter { return exists.updateValue(true, forKey: $0) == nil })
// limitation: allows at most base36 radix
guard case let radix = uniqueAlphabet.count
where radix < 37 else {
return (-1, nil)
}
// begin brute-force attempts
for i in start..<start+numAttempts {
let baseStr = String(i, radix: radix).characters
.flatMap { Int(String($0), radix: radix) }
.map { String(uniqueAlphabet[$0]) }
.joinWithSeparator("")
// construct attempt of correct length
let attempt = padToTemplate(str: baseStr,
withTemplate: String(count: wordLength, repeatedValue: alphabet.first!))
// log
//print(i, attempt)
// test attempt
if isCorrectPassword(attempt) { return (i, attempt) }
}
return (start+numAttempts, nil) // next to test
}
Example usage
Example usage #1
// unknown content closure
let someHashBashing : (String) -> Bool = {
return $0 == "ask"
}
// setup alphabet
let alphabet = [Character]("abcdefghijklmnopqrstuvwxyz".characters)
// any success for 500 attempts?
if case (let i, .Some(let password)) =
bruteForce(someHashBashing, forAlphabet: alphabet,
forWordLength: 3, forNumberOfAttempts: 500) {
print("Password cracked: \(password) (attempt \(i))")
} /* Password cracked: ask (attempt 478) */
Example usage #2 (picking up one failed "batch" with another)
// unknown content closure
let someHashBashing : (String) -> Bool = {
return $0 == "axk"
}
// setup alphabet
let alphabet = [Character]("abcdefghijklmnopqrstuvwxyz".characters)
// any success for 500 attempts?
let firstAttempt = bruteForce(someHashBashing, forAlphabet: alphabet,
forWordLength: 3, forNumberOfAttempts: 500)
if let password = firstAttempt.1 {
print("Password cracked: \(password) (attempt \(firstAttempt.0))")
}
// if not, try another 500?
else {
if case (let i, .Some(let password)) =
bruteForce(someHashBashing, forAlphabet: alphabet,
forWordLength: 3, forNumberOfAttempts: 500,
startingFrom: firstAttempt.0) {
print("Password cracked: \(password) (attempt \(i))")
} /* Password cracked: axk (attempt 608) */
}