I have a custom array used by a calculator, the array acts as an RPN stack. Many real life RPN calculators allow manipulation of the stack, and I am working on the same. To get an idea an example stack would be:
["1", "2", "+"] which would evaluate to 3. The core of it is the operations follow the operands.
["2", "1", "2", "+" "x"] evaluates to 6
["3.1415", "sin"] evaluates to 0
I would like to be able to "roll my stack" As in take the currently evaluated expression at the very end of the stack and roll it to the front. The problem is the operators, as the amount of binary and unary operators changes the way the stack is rotated.
["100", "1", "2", "+"]still would evaluate to 3, because 100 has not been accessed by an operator.
I need a way to recursively roll the stack, to take the last set of evaluated operands and roll it in front off all the operands yet to be evaluated.
Example:
["100", "1", "2", "+"] would roll to ["1", "2", "+", "100",] then ["100", "2", "1", "2", "+" "x"]would roll to ["2", "1", "2", "+" "x", "100"] and ["100", "3.1415", "sin"] would roll to ["3.1415", "sin","100"]
I am having problem with this, and the recursion of it. Currently I am doing this:
// safeRoll
func rollOpStack() {
var lastIndex = opStack.count - 1
var tmp: Op = opStack[lastIndex]
switch tmp {
case .UnaryOperation:
if opStack.count >= 2 {
var tmp2: Op = opStack[lastIndex - 1]
var appender: [Op] = [tmp2, tmp]
opStack.removeLast()
opStack.removeLast()
var appended: [Op] = appender + opStack
opStack = appended
}
case .BinaryOperation:
if opStack.count >= 3 {
var tmp2: Op = opStack[lastIndex - 1]
var tmp3: Op = opStack[lastIndex - 2]
var appender: [Op] = [tmp3, tmp2, tmp]
opStack.removeLast()
opStack.removeLast()
opStack.removeLast()
var appended: [Op] = appender + opStack
opStack = appended
}
default:
if opStack.count > 0 {
opStack.removeLast()
println(opStack)
opStack.insert(tmp, atIndex: 0)
}
}
}
This works... until more binary or unary operators are applied than just one, because the function is not recursive. How can I roll the stack recursively, and ensure that the entirety of the last evaluated list is rolled to the front of the stack? Thanks for the help.
This should do the trick:
func beginningIndexForOperationEndingAt(index: Int) -> Int? {
if index < 0 || index >= opStack.count {
return nil
}
switch opStack[index] {
case .UnaryOperation:
return beginningIndexForOperationEndingAt(index-1)
case .BinaryOperation:
if let firstOp = beginningIndexForOperationEndingAt(index-1) {
return beginningIndexForOperationEndingAt(firstOp-1)
}
return nil
default:
return index
}
}
If you call beginningIndexForOperationEndingAt(opStack.count-1) you will get the starting index for the last full operation, which you can then splice from the end of the array to the beginning
func rollOpStack() {
if let index = beginningIndexForOperationEndingAt(opStack.count-1) {
var newArray = opStack[index..<opStack.count]
newArray += opStack[0..<index]
var i = 0
for op in newArray {
opStack[i++] = op
}
} else {
//No complete operation...
}
}
Let me know if you run into any issues. Hope this helps! If it does, please don't forget to mark my answer as accepted! Thanks.
Related
I'm taking an array of ([(String?, String)]) and want to drop the last 3 or 5 elements of the non-optional String, depending on the String.
If I know I'm dropping 5 I use
let strippedName = (data.map{ ($0.1).dropLast(5) } ).map{ String($0) }
however, as I said this is a conditional thing depending on the string within the array.
When I try
let strippedName = (data.map{
if ($0.1 == "a") {
return $0.1.first!
} else {
return $0.1.last!
}
} ).map{ String($0) }
(which is obviously not quite the finished article for my needs).
The example will crash on some inputs, that is irrelevant. I need to have an if statement in the map above (the detail of the if statement is not relevant). An alternative is any way to have a conditional map function in Swift (as is the question title).
As above, my requirement is "I'm taking an array of ([(String?, String)]) and want to drop the last 3 or 5 elements of the non-optional String, depending on the String."
I get an error on the last String() cast - Ambiguous use of init.
How can I take my array and use an if statement within my first map?
Another way to solve it is to use reduce
let strippedName = data.reduce(into: []) { if $1.1 == "a" {$0.append($1.1)}}
Update
Trying to solve this as OP wants by using an if in the mapping. For my example here I use compactMap rather than map, this way anything that doesn't satisfy the if condition will be excluded from the array.
Since the return type of the closure is define to be String I don't need to do a second mapping
let strippedName = data.compactMap { t -> String? in
if t.1 == "a" {
return t.1
}
return nil
}
Swift could not infer the complex closure return type, hence it is showing you the error "Ambiguous use of init()"
Specifying the return type explicitly solves the issue.
let data: [(String?, String)] = [("a","bBCDEA"), ("b","a"), (nil,"CBCDEB"), ("d","aBCDEF"), (nil,"a"), ("f","FBCDEC")]
var strippedName = (data.map { tuple -> String.Element in
if (tuple.1 == "a") {
return tuple.1.first!
} else {
return tuple.1.last!
}
}).map{ String($0) }
print(strippedName) // ["A", "a", "B", "F", "a", "C"]
strippedName = (data.map{ ($0.1).dropLast(5) } ).map{ String($0) }
print(strippedName) // ["b", "", "C", "a", "", "F"]
I think the problem is with anonimous closure and type inference, however, to get what you're trying in your second chunk of code, you can use filter instead of conditionals. With this you won't get error:
let strippedName = data.filter { $0.1 == "a" }.map { $0.1 }
and you can add dropLast to the end.
Here is my code so far
var counter = 0
for i in 0...9 {
var val = NamePicker()
// array to find duplicates
var buttonValues = ["", "", "", "", "", "", "", "", "", ""] // array for button names
buttonValues.insert(val, at: counter)
print(buttonValues[counter])
counter += 1
}
This code is putting 10 string values into my array. What I would like to do is find a way to check each value in my array. for eample if my end result array is ["a","a","a","b","b","c","c","e","f","c"] I want to see if there is a triple of the same name(single and duplicates are fine). However if there is a triple I would like to change the 3rd value to another val from my NamePicker() function.
so with my array of
["a","a","a","b","b","c","c","e","f","c"]
there are 3 "a" and 3 "c", having two of the same is ok, i would like to change the 3rd to a new values and if the new value makes another triple it will change until there are no more triples.
so that array could possible have an end result of
["a","a","f","b","b","c","c","e","f","z"]
this is where the triples where changed.
Any help on how to do this efficiently?
Both options below asume that your NamePciker() function can generate at least 5 distinct values so there exists an array that satisfies your requirement.
Your requirement is better handled by not generating so many duplicates to begin with. If all you want is an array of names when each name cannot be repeated more than twice, try this:
var buttonValues = [String]()
var dict = [String: Int]()
while buttonValues.count < 10 {
let name = NamePicker()
let count = dict[name] ?? 0
guard count < 2 else { continue }
buttonValues.append(name)
dict[name] = count + 1
}
If you already have the array and want to correct it, do this:
var buttonValues = ["a","a","a","b","b","c","c","e","f","c"]
// Scan the array to tally how many times each name appears
var totalDict = [String: Int]()
buttonValues.forEach { totalDict[$0] = (totalDict[$0] ?? 0) + 1 }
// Now scan it again to update names that appear too many times
var runningDict = [String: Int]()
for (index, value) in buttonValues.enumerated() {
let count = runningDict[value] ?? 0
if count >= 2 {
while true {
let newValue = NamePicker()
let newTotal = (totalDict[newValue] ?? 0) + 1
if newTotal < 3 {
buttonValues[index] = newValue
totalDict[newValue] = newTotal
break
}
}
} else {
runningDict[value] = count + 1
}
}
Dictionary is the best way I think. Have the key be the character and the value be the count of that character. Your runtime will be O(n) since you only have to run through each input once. Here is an example:
let chars = ["a","a","a","b","b","c","c","e","f","c"]
var dict = [String: Int]()
for char in chars {
//If already in Dictionary, increase by one
if var count = dict[char] {
count += 1
dict[char] = count
} else {//else is not in the dictionary already, init with 1
dict[char] = 1
}
}
Output:
["b": 2, "e": 1, "a": 3, "f": 1, "c": 3]
Now I'm not sure how you want to replace the value that's the same character for a third time, but this is probably the best way to group the strings to determine which are over the limit.
Instead of inserting the wrong value and then checking if the values are correct, I would suggest to automatically create the correct array.
//array for button names
var buttonValues = Array<String>()
//tracks what value has been inserted how many times
var trackerDict = [String: Int]()
for i in 0...9 {
//we initialize a new variable that tells us if we found a valid value (if the value has not been inserted 2 times already)
var foundValidValue = false
while !foundValidValue{
var val = NamePicker()
//now we check if the value exists and if it is inserted less than 2 times
if let count = trackerDict[val] {
if count < 2 {
foundValidValue = true
}
}
//if we found the value, we can add it
if foundValidValue {
trackerDict[val] = (trackerDict[val] ?? 0) + 1
buttonValues.append(val)
}
//if we did not find it, we just run through the loop again
}
}
I added a dictionary because it is faster to keep track of the count in a dictionary than counting the number of occurrences in the array every time.
#IBAction func endTurn(sender: UIButton) {
let index: Int = Int (arc4random_uniform(UInt32(coins.count)))
var i = Int(arc4random_uniform((3)))
for i; i < 3; i++ {
coins[i].hidden = true
coins.removeAtIndex(i)
println(i)
}
}
I have 21 coins. It's array of buttons (#IBOutlet var coins: [UIButton]!). When i press "endTurn", the coins hidden. But when i have 3 coins or less, i get the fatal error (the line: coins[i].hidden = true).
What i need do?
Thanks...
Removing from an array while you iterate through it is a big no-no and probably not doing what you want it to.
var i = 0
var arr = ["1", "2", "3", "4", "5"]
for i; i < 3; i++ {
arr.removeAtIndex(i)
}
print(arr)
prints ["2", "4"]
Because if you look at the code, you remove at index 0, so your array is now ["2", "3", "4"], then you increment i, and remove at index 1. Since the array shifted, index 1 is "3", and you are skipping over "2".
Use this information in conjunction with the other answers
You are trying to access an index of the array that is out of bounds. So adding a check that the i is in the range of the array should prevent a crash.
if(i < coins.length) {
coins[i].hidden = true
coins.removeAtIndex(i)
}
An array with 3 elements [0, 1, 2] goes from index 0-2 (the first index of an array is 0).
This loop:
for i; i < 3; i++ {
coins[i].hidden = true
coins.removeAtIndex(i) // <-- here
println(i)
}
On every iteration you delete a coin. So you can delete up to 3 coins in total, when arc4random_uniform returns 0. When you have 3 (or fewer) coins left, you are bound to run into an index out of range problem.
This is an Array Concurrent Modification. And it's dangerous.
You should not modify the length of an array while you are iterating it. You could end up in a scenario where you are accessing the element at an index that no longer exists.
Let Swift solve the problem for you with the removeRange method. The following snippet should show you how to fix your code.
var animals = ["dog", "cat", "duck", "eagle", "cow"]
let from = 1
let to = 2
for i in from...to {
println(i) // will print `1` and `2`
}
animals.removeRange(from...to) // -> ["dog", "eagle", "cow"]
Hope this helps.
Say I have two arrays:
let arrayOne = ["Hi", "Hi", "Hello", "Not Hey", "Howdy", "Hi"]
let arrayTwo = ["Hi", "Hello", "Hey", "Not Howdy", "Hi", "Hi"]
and I have this for loop that gets the percent similarity of the doubles:
var matches = 0
for (index, item) in enumerate(arrayOne) {
if item == arrayTwo[index] {
matches++
}
}
However, what if those arrays are longer and I want data points of those similarities instead of one single calculation. What kind of a function could I write where it takes the first 5 elements off the array, returns their similarity with the for loop, and moves on to the next 5? ie. it would be a function that takes two string arrays and returns an array of doubles. I am sure this is a simple question but I do not know how to approach taking 5 array elements at a time and then returning an array of doubles (like data points the the string array similarities).
I’m not clear quite what you’re asking, however, you might find playing around with zip, map, and reduce helpful.
For example, you could rewrite your original loop like this (assuming Swift 2.0, you’d have to rearrange slightly for 1.2):
zip(arrayOne, arrayTwo).reduce(0) { $0 + ($1.0 == $1.1 ? 1 : 0) }
// returns 4
zip creates a new sequence of the pairs of elements at each corresponding position.
reduce takes a starting value, then keeps a running value by applying a function to the current value and the next value in the sequence – bearing in mind this is a sequence of pair elements, you want to add 1 when they are the same, 0 when they aren’t. This then gives you a count of the positions where both match.
If instead you wanted an array, with true representing a match at that point, and false if different, you could use map:
zip(arrayOne, arrayTwo).map(==)
// returns [true, false, false, false, false, true]
If on the other hand you wanted a list of the differences in string length between the two strings at each position, you could change it to:
zip(arrayOne, arrayTwo).map { (a,b) in
a.characters.count - b.characters.count
}
// returns [0, -3, 2, -2, 3, 0]
As some have suggested, Set might help, e.g.:
let commonElements = Set(arrayOne).intersect(arrayTwo)
// returns strings present in both e.g. {"Hi", "Hello”}
This is a good approach if you are OK treating your data as a set i.e. order doesn’t matter and duplicates can be ignored. If order and dupes do matter, you probably have to stick with arrays.
Take a look at this. I think it does what you are asking. By introducing a count variable to keep track of the number of items you have processed, you will know when to update your counts array:
var matches = 0
let arrayOne = ["Hi", "Hi", "Hello", "Not Hey", "Howdy", "Hi", "a", "b", "c"]
let arrayTwo = ["Hi", "Hello", "Hey", "Not Howdy", "Hi", "Hi", "a", "B", "C"]
var count = 0
var counts:[Int] = []
for (index, item) in enumerate(arrayOne) {
if item == arrayTwo[index] {
matches++
}
// Have we done 5? If so, time to update counts array
if ++count == 5 {
counts.append(matches)
count = 0
matches = 0
}
}
// If we didn't have 5, just append the matches for the remaining items
if count > 0 {
counts.append(matches)
}
println(counts) // prints "[1, 2]"
Right - so I think I understand what you're looking for: you want to have a matches function like the one you've written that works for chunks of a certain number of elements. First off, you're going to need a chunk function. There's a good discussion of them here, but they're for arrays, and you're going to want to zip your two arrays together here, so you'll need one for SequenceType. This works:
public extension SequenceType {
/// Returns an array of arrays of n non-overlapping elements of self
/// - Parameter n: The size of the chunk
/// ```swift
/// [1, 2, 3, 4, 5].chunk(2)
///
/// [[1, 2], [3, 4], [5]]
/// ```
func chunk(_ n: Int) -> [[Generator.Element]] {
var g = self.generate()
var ret: [[Generator.Element]] = [[]]
while let next = g.next() {
if ret.last!.count < n {
ret[ret.endIndex.predecessor()].append(next)
} else {
ret.append([next])
}
}
return ret
}
}
Then, you need a function that counts the matches in two arrays. You could inline it with a closure, or you could define it separately, it doesn't make much of a difference.
func matchesEvery<
S0 : SequenceType,
S1 : SequenceType,
T : Equatable where
S0.Generator.Element == T,
S1.Generator.Element == T
>(_ n: Int, s0: S0, s1: S1) -> [Int] {
return zip(s0, s1)
.chunk(5)
.map { $0.reduce(0) { $1.0 == $1.1 ? $0 + 1 : $0 } }
}
That will return:
let arrayOne = ["Hi", "Hi", "Hello", "Not Hey", "Howdy", "Hi"]
let arrayTwo = ["Hi", "Hello", "Hey", "Not Howdy", "Hi", "Hi"]
matchesEvery(5, s0: arrayOne, s1: arrayTwo) // [1, 1]
Since in the first five there's one match, and in the last there is one as well.
I can't seem to figure out how to do this even though I've searched through documentation.
I'm trying to figure out how to convert a character at an index in an array to an integer.
For example, say I have a character array named "container", I can't figure out how to do:
var number:Integer = container[3]
Thanks for the help!
Swift doesn't make it easy to convert between primitive and typed representations of things. Here's an extension that should help in the meantime:
extension Character {
func utf8Value() -> UInt8 {
for s in String(self).utf8 {
return s
}
return 0
}
func utf16Value() -> UInt16 {
for s in String(self).utf16 {
return s
}
return 0
}
func unicodeValue() -> UInt32 {
for s in String(self).unicodeScalars {
return s.value
}
return 0
}
}
This allows you to get pretty close to what you want:
let container : Array<Character> = [ "a", "b", "c", "d" ]
/// can't call anything here, subscripting's also broken
let number = container[2]
number.unicodeValue() /// Prints "100"
For any engineers that come across this question, see rdar://17494834
I am not sure that it is effective or not but at least it worked. I converted Character to String then to Int.
String(yourCharacterInArray).toInt()
You may try this:
var container = "$0123456789"
var number:Int = Array(container.utf8).map { Int($0) }[3]
It's totally ugly, but it does the job. Also it is a bit computational expensive (O(n) each time one access a character in a string). Still this can be a trick to get back a way to build the CStrings:
typealias CString = Array<CChar>
func toCString(string: String) -> CString {
return Array(string.utf8).map { CChar($0) } + [0]
}
var cString = toCString("$ 0123456789")
println("The 2nd character in cString has value \(cString[1])") // It outputs 32
or without implementing a function:
var container = "$ 0123456789"
var containerAsCString = Array(container.utf8).map { CChar($0) } + [0]
println("The 2nd character in container has value \(containerAsCString[1])") // It outputs 32
Why not just for loop the array and convert everything to Int?
https://developer.apple.com/Library/mac/documentation/General/Reference/SwiftStandardLibraryReference/index.html
Why not just convert the character to String, get the unicodeScalars for it and extract the .value on the scalar?
something like:
var chr: [Character] = ["C", "B", "A"]
for a in String(chr[1]).unicodeScalars {
println(a.value)}
For me worked something like:
"\(container[3])".toInt()