Sum integers from a key value until there is one-digit - swift

I need a function that both reduces integers to a single digit and accepts strings. Variants using reduce would also be appreciated.
let dict: [Character:Int] = ["a": 1, "j":1, "s":1, "b":2, "k":2, "t":2, "c":3, "l":3, "u":3, "d":4, "m":4, "v":4, "e":5, "n":5, "w":5, "f":6, "o":6, "x":6, "g":7, "p":7, "y":7, "h":8, "q":8, "z":8, "i":9, "r":9]
This function accepts strings and reduces by summing, but does not reduce to a single digit.
func f1(_ str: String) -> Int {
return str.reduce(0) { $0 + (dict[$1] ?? 0) }
}
f1("lighthouse") //52
This function reduces to a single digit but does not accepts strings.
func f2(_ n: Int) -> Int {
return (1 + ((n-1) % 9))
}
f2(52) //7

You just need to combine your both methods in the return statement:
return 1 + (str.reduce(0) {$0 + (dict[$1] ?? 0)} - 1) % 9

Here is a function that converts a string into a single digit Int.
Input: "9l231ffi1j"
func f1(_ str: String) -> Int {
return str.reduce(0) { $0 + ((Int(String($1)) ?? dict[$1]) ?? 0) }
}
func f3(_ input : String) -> Int {
return f2(f1(input))
}
f3("lighthouse") //7
f3("9l231ffi1j") //5

Thanks to Leo's answer in this question and Martin's answer in my other related [question] I was able to produce a standard version using Swift's for in loop without shorthand {$0 $1} and reduce().
let n = "lighthouse"
var nScore = 0
for i in n.indices {
let curChar = n[i]
let curVal = dict[curChar, default: 0]
nScore = 1 + (nScore + curVal - 1) % 9
}
nScore //7

Related

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)

How to execute multiplications and/or divisions in the right order?

I am doing a simple calculator, but when performing the multiplication and division, my code doesn't make them a priority over plus and minus.
When doing -> 2 + 2 * 4, result = 16 instead of 10...
How to conform to the math logic inside my switch statement?
mutating func calculateTotal() -> Double {
var total: Double = 0
for (i, stringNumber) in stringNumbers.enumerated() {
if let number = Double(stringNumber) {
switch operators[i] {
case "+":
total += number
case "-":
total -= number
case "÷":
total /= number
case "×":
total *= number
default:
break
}
}
}
clear()
return total
}
Assuming you want a generalised and perhaps extensible algorithm for any arithmetic expression, the right way to do this is to use the Shunting Yard algorithm.
You have an input stream, which is the numbers and operators as the user typed them in and you have an output stream, which is the same numbers and operators but rearranged into reverse Polish notation. So, for example 2 + 2 * 4 would be transformed into 2 2 4 * + which is easily calculated by putting the numbers on a stack as you read them and applying the operators to the top items on the stack as you read them.
To do this the algorithm has an operator stack which can be visualised as a siding (hence "shunting yard") into which low priority operators are shunted until they are needed.
The general algorithm is
read an item from the input
if it is a number send it to the output
if the number is an operator then
while the operator on the top of the stack is of higher precedence than the operator you have pop the operator on the stack and send it to the output
push the operator you read from input onto the stack
repeat the above until the input is empty
pop all the operators on the stack into the output
So if you have 2 + 2 * 4 (NB top of the stack is on the left, bottom of the stack is on the right)
start:
input: 2 + 2 * 4
output: <empty>
stack: <empty>
step 1: send the 2 to output
input: + 2 * 4
output: 2
stack: <empty>
step 2: stack is empty so put + on the stack
input: 2 * 4
output: 2
stack: +
step 3: send the 2 to output
input: * 4
output: 2 2
stack: +
step 4: + is lower priority than * so just put * on the stack
input: 4
output: 2 2
stack: * +
step 5: Send 4 to output
input:
output: 2 2 4
stack: * +
step 6: Input is empty so pop the stack to output
input:
output: 2 2 4 * +
stack:
The Wikipedia entry I linked above has a more detailed description and an algorithm that can handle parentheses and function calls and is much more extensible.
For completeness, here is an implementation of my simplified version of the algorithm
enum Token: CustomStringConvertible
{
var description: String
{
switch self
{
case .number(let num):
return "\(num)"
case .op(let symbol):
return "\(symbol)"
}
}
case op(String)
case number(Int)
var precedence: Int
{
switch self
{
case .op(let symbol):
return Token.precedences[symbol] ?? -1
default:
return -1
}
}
var operation: (inout Stack<Int>) -> ()
{
switch self
{
case .op(let symbol):
return Token.operations[symbol]!
case .number(let value):
return { $0.push(value) }
}
}
static let precedences = [ "+" : 10, "-" : 10, "*" : 20, "/" : 20]
static let operations: [String : (inout Stack<Int>) -> ()] =
[
"+" : { $0.push($0.pop() + $0.pop()) },
"-" : { $0.push($0.pop() - $0.pop()) },
"*" : { $0.push($0.pop() * $0.pop()) },
"/" : { $0.push($0.pop() / $0.pop()) }
]
}
struct Stack<T>
{
var values: [T] = []
var isEmpty: Bool { return values.isEmpty }
mutating func push(_ n: T)
{
values.append(n)
}
mutating func pop() -> T
{
return values.removeLast()
}
func peek() -> T
{
return values.last!
}
}
func shuntingYard(input: [Token]) -> [Token]
{
var operatorStack = Stack<Token>()
var output: [Token] = []
for token in input
{
switch token
{
case .number:
output.append(token)
case .op:
while !operatorStack.isEmpty && operatorStack.peek().precedence >= token.precedence
{
output.append(operatorStack.pop())
}
operatorStack.push(token)
}
}
while !operatorStack.isEmpty
{
output.append(operatorStack.pop())
}
return output
}
let input: [Token] = [ .number(2), .op("+"), .number(2), .op("*"), .number(4)]
let output = shuntingYard(input: input)
print("\(output)")
var dataStack = Stack<Int>()
for token in output
{
token.operation(&dataStack)
}
print(dataStack.pop())
If you only have the four operations +, -, x, and ÷, you can do this by keeping track of a pendingOperand and pendingOperation whenever you encounter a + or -.
Then compute the pending operation when you encounter another + or -, or at the end of the calculation. Note that + or - computes the pending operation, but then immediately starts a new one.
I have modified your function to take the stringNumbers, operators, and initial values as input so that it could be tested independently in a Playground.
func calculateTotal(stringNumbers: [String], operators: [String], initial: Double) -> Double {
func performPendingOperation(operand: Double, operation: String, total: Double) -> Double {
switch operation {
case "+":
return operand + total
case "-":
return operand - total
default:
return total
}
}
var total = initial
var pendingOperand = 0.0
var pendingOperation = ""
for (i, stringNumber) in stringNumbers.enumerated() {
if let number = Double(stringNumber) {
switch operators[i] {
case "+":
total = performPendingOperation(operand: pendingOperand, operation: pendingOperation, total: total)
pendingOperand = total
pendingOperation = "+"
total = number
case "-":
total = performPendingOperation(operand: pendingOperand, operation: pendingOperation, total: total)
pendingOperand = total
pendingOperation = "-"
total = number
case "÷":
total /= number
case "×":
total *= number
default:
break
}
}
}
// Perform final pending operation if needed
total = performPendingOperation(operand: pendingOperand, operation: pendingOperation, total: total)
// clear()
return total
}
Tests:
// 4 + 3
calculateTotal(stringNumbers: ["3"], operators: ["+"], initial: 4)
7
// 4 × 3
calculateTotal(stringNumbers: ["3"], operators: ["×"], initial: 4)
12
// 2 + 2 × 4
calculateTotal(stringNumbers: ["2", "4"], operators: ["+", "×"], initial: 2)
10
// 2 × 2 + 4
calculateTotal(stringNumbers: ["2", "4"], operators: ["×", "+"], initial: 2)
8
// 17 - 2 × 3 + 10 + 7 ÷ 7
calculateTotal(stringNumbers: ["2", "3", "10", "7", "7"], operators: ["-", "×", "+", "+", "÷"], initial: 17)
22
First you have to search in the array to see if there is a ÷ or × sign.
Than you can just sum or subtract.
mutating func calculateTotal() -> Double {
var total: Double = 0
for (i, stringNumber) in stringNumbers.enumerated() {
if let number = Double(stringNumber) {
switch operators[i] {
case "÷":
total /= number
case "×":
total *= number
default:
break
}
//Remove the number from the array and make another for loop with the sum and subtract operations.
}
}
clear()
return total
}
This will work if you are not using complex numbers.
If you don't care speed, as it's running by a computer and you may use the machine way to handle it. Just pick one feasible calculate to do it and then repeat until every one is calculated.
Just for fun here. I use some stupid variable and function names.
func evaluate(_ values: [String]) -> String{
switch values[1] {
case "+": return String(Int(values[0])! + Int(values[2])!)
case "-": return String(Int(values[0])! - Int(values[2])!)
case "×": return String(Int(values[0])! * Int(values[2])!)
case "÷": return String(Int(values[0])! / Int(values[2])!)
default: break;
}
return "";
}
func oneTime(_ string: inout String, _ strings: [String]) throws{
if let first = try NSRegularExpression(pattern: "(\\d+)\\s*(\(strings.map{"\\\($0)"}.joined(separator: "|")))\\s*(\\d+)", options: []).firstMatch(in: string , options: [], range: NSMakeRange(0, string.count)) {
let tempResult = evaluate((1...3).map{ (string as NSString).substring(with: first.range(at: $0))})
string.replaceSubrange( Range(first.range(at: 0), in: string)! , with: tempResult)
}
}
func recursive(_ string: inout String, _ strings: [String]) throws{
var count : Int!
repeat{ count = string.count ; try oneTime(&string, strings)
} while (count != string.count)
}
func final(_ string: inout String, _ strings: [[String]]) throws -> String{
return try strings.reduce(into: string) { (result, signs) in
try recursive(&string, signs)
}}
var string = "17 - 23 + 10 + 7 ÷ 7"
try final(&string, [["×","÷"],["+","-"]])
print("result:" + string)
Using JeremyP method and the Shunting Yard algorithm was the way that worked for me, but I had some differences that had to do with the Operator Associativity(left or right priority) so I had to work with it and I developed the code, which is based on JeremyP answer but uses arrays.
First we have the array with the calculation in Strings, e.g.:
let testArray = ["10","+", "5", "*" , "4", "+" , "10", "+", "20", "/", "2"]
We use the function below to get the RPN version using the Shunting Yard algorithm.
func getRPNArray(calculationArray: [String]) -> [String]{
let c = calculationArray
var myRPNArray = [String]()
var operandArray = [String]()
for i in 0...c.count - 1 {
if c[i] != "+" && c[i] != "-" && c[i] != "*" && c[i] != "/" {
//push number
let number = c[i]
myRPNArray.append(number)
} else {
//if this is the first operand put it on the opStack
if operandArray.count == 0 {
let firstOperand = c[i]
operandArray.append(firstOperand)
} else {
if c[i] == "+" || c[i] == "-" {
operandArray.reverse()
myRPNArray.append(contentsOf: operandArray)
operandArray = []
let uniqOperand = c[i]
operandArray.append(uniqOperand)
} else if c[i] == "*" || c[i] == "/" {
let strongOperand = c[i]
//If I want my mult./div. from right(eg because of parenthesis) the line below is all I need
//--------------------------------
// operandArray.append(strongOperand)
//----------------------------------
//If I want my mult./div. from left
let lastOperand = operandArray[operandArray.count - 1]
if lastOperand == "+" || lastOperand == "-" {
operandArray.append(strongOperand)
} else {
myRPNArray.append(lastOperand)
operandArray.removeLast()
operandArray.append(strongOperand)
}
}
}
}
}
//when I have no more numbers I append the reversed operant array
operandArray.reverse()
myRPNArray.append(contentsOf: operandArray)
operandArray = []
print("RPN: \(myRPNArray)")
return myRPNArray
}
and then we enter the RPN array in the function below to calculate the result. In every loop we remove the numbers and the operand used before and we import the previous result and two "p" in the array so in the end we are left with the solution and an array of "p".
func getResultFromRPNarray(myArray: [String]) -> Double {
var a = [String]()
a = myArray
print("a: \(a)")
var result = Double()
let n = a.count
for i in 0...n - 1 {
if n < 2 {
result = Double(a[0])!
} else {
if a[i] == "p" {
//Do nothing else. Calculations are over and the result is in your hands!!!
} else {
if a[i] == "+" {
result = Double(a[i-2])! + Double(a[i-1])!
a.insert(String(result), at: i-2)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.insert("p", at: 0)
a.insert("p", at: 0)
} else if a[i] == "-" {
result = Double(a[i-2])! - Double(a[i-1])!
a.insert(String(result), at: i-2)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.insert("p", at: 0)
a.insert("p", at: 0)
} else if a[i] == "*" {
result = Double(a[i-2])! * Double(a[i-1])!
a.insert(String(result), at: i-2)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.insert("p", at: 0)
a.insert("p", at: 0)
} else if a[i] == "/" {
result = Double(a[i-2])! / Double(a[i-1])!
a.insert(String(result), at: i-2)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.remove(at: i - 1)
a.insert("p", at: 0)
a.insert("p", at: 0)
} else {
// it is a number so do nothing and go the next one
}
}//no over yet
}//n>2
}//iterating
return result
}//Func

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

Add / subtract characters as Int in Swift

I need to implement an algorithm to check if an input is valid by calculating a modulo of a String.
The code in Kotlin:
private val facteurs = arrayOf(7, 3, 1)
private fun modulo(s: String): Int {
var result = 0
var i = -1
var idx = 0
for (c in s.toUpperCase()) {
val value:Int
if (c == '<') {
value = 0
} else if (c in "0123456789") {
value = c - '0'
} else if (c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
value = c.toInt() - 55
} else {
throw IllegalArgumentException("Unexpected character: $c at position $idx")
}
i += 1
result += value * facteurs[i % 3]
idx += 1
}
return result % 10
}
This implies doing math operations on the characters.
Is there an elegant way to do this in Swift 3 and 4?
I tried some cumbersome constructs like this :
value = Int(c.unicodeScalars) - Int("0".first!.unicodeScalars)
But it does not even compile.
I'm currently using Swift 4 with XCode9, but Swift3 answer is welcome too.
You can enumerate the unicodeScalars view of a string together
with the running index, use switch/case pattern matching,
and access the numeric .value of the unicode scalar:
func modulo(_ s: String) -> Int? {
let facteurs = [7, 3, 1]
var result = 0
for (idx, uc) in s.uppercased().unicodeScalars.enumerated() {
let value: UInt32
switch uc {
case "<":
value = 0
case "0"..."9":
value = uc.value - UnicodeScalar("0").value
case "A"..."Z":
value = uc.value - UnicodeScalar("A").value + 10
default:
return nil
}
result += Int(value) * facteurs[idx % facteurs.count]
}
return result % 10
}
This compiles with both Swift 3 and 4. Of course you could also
throw an error instead of returning nil for invalid input.
Note that "<", "0", "9" etc.
in the switch statement are inferred from the context as UnicodeScalar,
not as String or Character, therefore "0"..."9" (in this context)
is a ClosedRange<UnicodeScalar> and uc can be matched against
that range.
Something like this works for me:
"A".utf16.first! + 2 //comes out to 67
Careful with the forced unwrap "!"
If you need the scalars value you can do
"A".unicodeScalars.first!.value + 2
More reading can be done on this here in the SPL.
For the c Character type value you could do this:
String(c).unicodeScalars.first!.value + 2
Here is an attempt to mod the function:
func modulo(s: String) -> Int? {
var result = 0
var factors = [7,3,1]
for (i, c) in s.uppercased().characters.enumerated() {
let char = String(c)
var val: Int
if char == "<" {
val = 0
} else if "0123456789".contains(char) {
val = Int(char.unicodeScalars.first!.value - "0".unicodeScalars.first!.value)
} else if "ABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(char) {
val = Int(char.unicodeScalars.first!.value - 55)
} else {
return nil
}
result += val * factors[(i) % 3]
}
return result % 10
}
This is in swift 3...in 4 I believe you can just iterate over the string without converting to Chars

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) */
}