Calculate the data received inside the recursive function - arrays

The function outputs via print() all possible combinations of the characters "abc". (Depending on the specified length)
I need to calculate this amount. I only managed to output these combinations one by one through print(). I left a comment in the right place of the code.
func allLexicographicRecur (_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int){
var length = string.count-1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
}else{
allLexicographicRecur(string, data, last, index+1)
}
}
}
func allLexicographic(_ l: Int) {
var alphabet = "abc"
var data = Array(repeating: "", count: l)
var string = alphabet.sorted()
var counter = 0
allLexicographicRecur(string, data, l-1, 0)
}
allLexicographic(3)
The function must somehow return the number of these combinations.
I would be very grateful for the help!
I managed to count only this way (but most likely it is not the best way to do it):
var count = 0
func allLexicographicRecur (_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int){
var length = string.count-1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
count += 1
}else{
allLexicographicRecur(string, data, last, index+1)
}
}
}
func allLexicographic(_ l: Int) {
var alphabet = "abc"
var data = Array(repeating: "", count: l)
var string = alphabet.sorted()
var counter = 0
allLexicographicRecur(string, data, l-1, 0)
}
allLexicographic(3)
print(count)

You do not need a global variable. There are at least two other options. You can add an inout parameter to allLexicographicRecur to keep track of the count or you can have allLexicographicRecur return its count.
Here's your code using a return value:
func allLexicographicRecur(_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int) -> Int {
let length = string.count - 1
var data = data
var count = 0
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
count += 1
} else {
count += allLexicographicRecur(string, data, last, index + 1)
}
}
return count
}
func allLexicographic(_ l: Int) -> Int {
let alphabet = "abc"
let data = Array(repeating: "", count: l)
let string = alphabet.sorted()
return allLexicographicRecur(string, data, l - 1, 0)
}
print(allLexicographic(3))
Here's your code updated to use an inout parameter.
func allLexicographicRecur(_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int, _ count: inout Int){
let length = string.count - 1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
print(data.joined()) // Displays a combination. It is necessary to somehow calculate.
count += 1
} else {
allLexicographicRecur(string, data, last, index + 1, &count)
}
}
}
func allLexicographic(_ l: Int) -> Int {
let alphabet = "abc"
let data = Array(repeating: "", count: l)
let string = alphabet.sorted()
var counter = 0
allLexicographicRecur(string, data, l - 1, 0, &counter)
return counter
}
print(allLexicographic(3))

You can not mange the count without global variable because of recursive function. so the method you wrote in question is perfect as per the output you want to have.

Related

How to convert Sequence to Array or is there an end of Sequence in Swift?

According to the documentation:
init(_ s: S) where Element == S.Element, S : Sequence
Creates an array containing the elements of a sequence.
struct Test: IteratorProtocol, Sequence {
let id: Int
init(_ id: Int) {
self.id = id
}
mutating func next() -> Test? {
id < 10 ? Test(id + 1) : nil
}
}
let test = Test(5)
let arr = Array(test)
It compiles. And doesn't even throw any runtime errors.
But instead of getting the array [5, 6, 7, 8, 9] as a result, I get an infinite loop! next() is called infinitely many times.
I thought that nil in next() is a natural indicator of the end of sequence. But apparently it's not.
self.id never changes, so it never reaches 10.
It should be something like this
struct Test: IteratorProtocol, Sequence {
var id: Int
init(_ id: Int) {
self.id = id
}
mutating func next() -> Test? {
defer { id += 1 }
return id < 10 ? self : nil
}
}
print(Array(Test(6)))
Another example
struct Countdown: Sequence, IteratorProtocol {
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}
let threeToGo = Countdown(count: 3)
for i in threeToGo {
print(i)
}
// Prints "3"
// Prints "2"
// Prints "1"
Appears, there is a built-in function, that completely suits the logic of my initial question in this post.
sequence(first:next:)
Returns a sequence formed from first and repeated lazy applications of next.
struct Test {
var id: Int
init(_ id: Int) {
self.id = id
}
}
let seq = sequence(first: Test(5), next: { test in
let id = test.id + 1
return id < 10 ? Test(id) : nil
})
let arr = Array(seq)

Increment value in an array of dictionaries

I'm trying to calculate the number of times a specific number is rolled in a set of six six-sided dice, to determine wether or not I have three of a kind, four of a kind, etc.
I can pull the face value of each die rolled and compare it to the faces on a 6 sided die but can't get the "qtyRolled" key/value to increment.
func rollDice() {
currentRoll.removeAll()
for _ in currentDiceArray {
let num: UInt32 = arc4random_uniform(UInt32(currentDieFaceArray.count))
let currentDieData = currentDieFaceArray[Int(num)]
let faceValue = currentDieData["faceValue"]
currentRoll.append(faceValue as! Int)
}
print(currentRoll)
getQtyOfDieFaces()
//checkForScoringCombos()
}
func getQtyOfDieFaces() {
for die in currentRoll {
for dieData in currentDieFaceArray {
var currentDieData = dieData
let qtyRolled = currentDieData["qtyRolled"] as! Int
let faceValue = currentDieData["faceValue"] as! Int
print("faceValue: \(faceValue)")
print("Die: \(die)")
if faceValue == die {
currentDieData["qtyRolled"] = qtyRolled + 1 as AnyObject
}
}
}
for currentDieData in currentDieFaceArray {
print(currentDieData["qtyRolled"]!)
}
}
Here are my data structures
var currentDieFaceArray = [[String:AnyObject]]()
var currentDiceArray:[[String:AnyObject]] = [[:]]
var currentRoll: [Int] = []
I'd recommend ditching the dictionaries unless you really need them, as you're really just dealing with properties of a struct/class. I'm going to assume you're using the currentDieFaceArray method so that you can make this generic for non-linear dice faces of other dimensions (e.g. you can have a four-sided dice with the face values [1, 4, 6, 8]). If this isn't the case, you can simplify further I'm sure with a simple array of counts. But here's an example with your method (probably has other possible optimisations).
class DieFaceDefn
{
let faceValue : Int
var countThisRoll : Int = 0
init(faceValue: Int)
{
self.faceValue = faceValue
}
}
var diceFaces: [DieFaceDefn] = []
let numberOfCurrentDice = 5
func setupDice()
{
diceFaces.append(DieFaceDefn(faceValue: 1))
diceFaces.append(DieFaceDefn(faceValue: 2))
...
}
var currentRoll: [Int] = []
func rollDice()
{
currentRoll.removeAll()
diceFaces.forEach { $0.countThisRoll = 0 }
for _ in 0..<numberOfCurrentDice
{
let num: UInt32 = arc4random_uniform(UInt32(diceFaces.count))
let currentDieData = diceFaces[Int(num)]
let faceValue = currentDieData.faceValue
currentRoll.append(faceValue)
currentDieData.countThisRoll += 1
}
print(currentRoll)
diceFaces.forEach { print("\($0.faceValue): \($0.countThisRoll)") }
}

Swift 3: Restrict .components array and use reduce

I try to get the first n parts of a path:
var rootPath = "/p1/p2"
var filePath = "/p1/p2/p3/p4/file.ext"
I want to get one more than used by rootPath = 3 folders deep:
result = "/p1/p2/p3/"
I try to use build-in array components, but I don't know how to restrict it to a specific number:
func getRootFolderRel(rootURL : URL, fileURL : URL, relIndex : Int) -> String {
let pccount = rootURL.pathComponents.count + 1
var count = 0
return fileURL.pathComponents.reduce("", { count += 1 < pccount ? $0.stringByAppendingPathComponent(pathComponent: $1) : $0 })
}
Any hints? (It should be as fast as possible) :-)
Added: (I wrote an working function, but it is not using internal commands. Everything is done by me, so I assume, it is very slow compared to solutions based on build-in functions).
func getRootFolderRel(rootURL : URL, fileURL : URL, relIndex : Int) -> String {
let pccount = rootURL.pathComponents.count + relIndex
var array = [String]()
var count = 0
while count < pccount && count < rootURL.pathComponents.count {
array.append(rootURL.pathComponents[count])
count += 1
}
let offset = count
while count < pccount && count < fileURL.pathComponents.count {
array.append(fileURL.pathComponents[count])
count += 1
}
return array.reduce("", { $0.stringByAppendingPathComponent($1) })
}

How would I go about categorizing items within an Array in swift?

In swift, I want to categorize items in an existing array and place them accordingly in one new string.
Here is an example of what I want to do:
originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
resultingString = "hotdog x 3, fries x 2, coke x 2"
How would I go about doing this?
Try this:
let originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
var dict = [String: Int]()
let resultString = originalArray.reduce(dict) { _, element in
if dict[element] == nil {
dict[element] = 1
} else {
dict[element]! += 1
}
return dict
}
.map { "\($0) x \($1)" }
.joinWithSeparator(", ")
If you want to keep the original order of the array (ie: hotdog, fries, coke), the code is slightly more complicated:
let originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
var dict = [String: (index: Int, count: Int)]()
let resultString = originalArray.enumerate()
.reduce(dict) { _ , e in
if let value = dict[e.element] {
dict[e.element] = (index: value.index, count: value.count + 1)
} else {
dict[e.element] = (index: e.index, count: 1)
}
return dict
}
.sort { return $0.1.index < $1.1.index }
.map { "\($0) x \($1.count)" }
.joinWithSeparator(", ")
print(resultString)
I think this will help you:
let originalArray = ["hotdog","fries","hotdog","coke","coke","fries","hotdog"]
var resultingString = ""
var counts:[String:Int] = [:]
for item in originalArray {
counts[item] = (counts[item] ?? 0) + 1
}
resultingString = counts.map { (key, value) -> String in
return "\(key) x \(value)"
}.joinWithSeparator(",")
print(resultingString)
Here is the output: coke x 2, hotdog x 3, fries x 2

outside array not affected from function appending within

I am working with arrays and I created a function that appends an array from within. However when I print the array, it still appears empty. What gives?
var queriesFinal : [String] = []
func queryValidator(search : String)
{
var letterSet = NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz ")
var numberSet = NSCharacterSet(charactersInString: "1234567890".uppercaseString)
var queriesTwo : [String] = search.lowercaseString.componentsSeparatedByCharactersInSet(letterSet)
for(var x = 0; x < queriesTwo.count; x++)
{
for(var y = 0; y < 10; y++)
{
var str = String(y)
if(queriesTwo[x] == str)
{
var numberStr = String(queriesTwo[x]) + "th"
queriesFinal.append(numberStr)
}
}
}
}
println(queriesFinal)
search = "Matt 8th"
queryValidator(search)
This code can run in playground..
I appreciate any help!
As mentioned by Mike S, you've made a small mistake println should be after your queryValidator, I've also added an optional in case your queryValidator search returns nil, also as mentioned by Zaph you don't need numberSet, so I removed it:
func queryValidator(search : String) -> [String]? {
let queriesTwo:[String] = search.lowercaseString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz "))
var queriesResult:[String] = []
for x in 0...queriesTwo.count-1 {
for y in 0...9 {
if(queriesTwo[x] == String(y)) {
queriesResult.append(String(queriesTwo[x]) + "th")
}
}
}
return queriesResult.count > 0 ? queriesResult : nil
}
var search = "Matt 8 less7"
if let queriesFinal = queryValidator(search) {
println(queriesFinal)
} else {
println("no result")
}
An alternative approach with Regular Expressions:
func queryValidator(search: String) -> [String] {
var queriesFinal:[String] = []
var nsSearch: NSString = search
let pattern = "(\\d+)"
var regEx = NSRegularExpression(pattern:pattern, options:nil, error:nil)
regEx?.enumerateMatchesInString(nsSearch, options:nil, range:NSMakeRange(0, nsSearch.length), usingBlock: { (result, flags, stop) in
let found = nsSearch.substringWithRange(result.range)
queriesFinal.append("\(found)th")
})
return queriesFinal
}
var result = queryValidator(search)
println("result: \(result)")
Output:
result: [8th, 7th, 6th]
For information on regular expressions see: Regular Expressions

Resources