Swift buttons hidden fatal error: Array index out of range - ios

#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.

Related

Pairing duplicate elements in an array

I am trying to pair the duplicate elements of an array and count the pairs.
When given array is : [10, 20, 20, 10, 10, 30, 50, 10, 20], I'm expecting numberOfPairs to be 3. Because there are 2 pairs of 10s and 1 pair of 20.
My "if condition" is checking if current element's index is first index or not. If it is not the last index, it means that there is a duplicate of the current element. So I'am adding 1 to numberOfPairs.
For input [10, 20, 20, 10, 10, 30, 50, 10, 20], my numberOfPairs is 2 but it should be 3.
For input [1 1 3 1 2 1 3 3 3 3], myNumberOfPairs is not printing at all? But instead it should be 4.
My What am I missing here?
func sockMerchant(n: Int, ar: [Int]) -> Int {
// Write your code here
var array = ar
var numberOfPairs = 0
for i in 0..<array.count {
var element = array[i]
let indexOfLastElement = array.lastIndex(of: element)
let indexOfFirstElement = array.firstIndex(of: element)
print("indexOfLastElement is \(indexOfLastElement)")
print("indexOfFirstElement is \(indexOfFirstElement)")
if indexOfFirstElement != indexOfLastElement {
numberOfPairs += 1
array.remove(at: indexOfFirstElement!)
array.remove(at: indexOfLastElement!)
continue
}
return numberOfPairs
}
return numberOfPairs
}
You're mutating your array by calling remove(at:) at the same time as you're accessing it which is why you're having these weird side effects.
I assume you're trying to solve a Leetcode task (or something similar), so I won't provide a solution upfront. My suggestion for you is to think of an algorithm that doesn't involve changing the contents of the List while you're reading these contents of that same List.
I'm agree with #MartinR that in such cases you should place breakpoints and go throught your code line by line, glad you've found your mistake by yourself.
But also in terms of performance, lastIndex and firstIndex are very heavy operations, because they may go thought all items and find nothing, which makes Big O notation of your algorithm around O(log n). In such cases dictionary is widely used(if you're not much limited with the space).
You can use value as a key and count as a value for a dictionary and count all items, then just sum like this:
func sockMerchant(ar: [Int]) -> Int {
ar.reduce(into: [Int:Int]()) { map, value in
map[value, default: 0] += 1
}.reduce(0) { sum, count in
sum + count.value / 2
}
}
So, I've solved the problem as below, thanks to #Vym and #Martin R.
func sockMerchant(n: Int, ar: [Int]) -> Int {
// Write your code here
var array = ar
var numberOfPairs = 0
var newArray = [Int]()
var done = false
for i in 0..<array.count {
let element = array[i]
let indexOfLastElement = array.lastIndex(of: element)
let indexOfFirstElement = array.firstIndex(of: element)
if indexOfFirstElement != indexOfLastElement {
newArray.append(element)
numberOfPairs = newArray.count/2
done = true
}
}
if done == true {
return numberOfPairs
}
return numberOfPairs
}

Swift 3 For loop compare and change

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.

NSRange array of odd or even count

I have an array of 977 of data, I've been trying to split it to 20 section in UITableView but i cannot get it correct, i am trying to do it dynamically. like if i got an array of 312 or 32 or 545 the equation should divide it, and add the last odd elements in array, I'm placing the new data in array of arrays.
So here is what I'm doing :
var dataof977 = Mydata()
var mutA = NSMutableArray()
for (var i = 0; i < 19; i++)
{
var halfArray : NSArray!
var theRange = NSRange()
theRange.location = i*19;
theRange.length = dataof977.afa.count / 19
halfArray = dataof977.afa.subarrayWithRange(theRange)
mutA.addObject(halfArray)
}
Note : dataof977 is reference of class and afa is a String array.
What am i missing here ?s
Three things:
You need to start each location where the previous left off. To do this, introduce a location variable to keep track of where you are in the original array.
Some of your sections will need more items since your count might not be a multiple of 20. I think your best best is to give the first n sections an extra item to make up for the leftovers.
Your loop needs to iterate 20 times, not 19. I have changed it to use for in which is better Swift style.
var mutA = NSMutableArray()
let sublength = dataof977.afa.count / 20
let leftovers = dataof977.afa.count % 20
// location of each new subarray
var location = 0
for i in 0..<20
{
var length = sublength
// The first sections will have 1 more each
if i < leftovers {
length++
}
let theRange = NSMakeRange(location, length)
let halfArray = dataof977.afa.subarrayWithRange(theRange)
mutA.addObject(halfArray)
// move location up past the items we just added
location += length
}
If you have the possibility to work with Swift arrays instead of NSArray, you can use stride to iterate by steps and divide an array in equal parts with the remainder elements in the last array:
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
extension Array {
func splitBy(subSize: Int) -> [[Element]] {
return 0.stride(to: self.count, by: subSize).map { startIndex in
let endIndex = startIndex.advancedBy(subSize, limit: self.count)
return Array(self[startIndex ..< endIndex])
}
}
}
let chunks = arr.splitBy(5)
print(chunks) // [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12]]
Note: this is for Swift 2.
You are only doing 19 iterations, not 20.
for var i = 0; i < 20; i++
You are taking one 19th of the data count, not one 20th. You are repeating the length calculation 19 times in the loop, which is redundant.
It would be easier to dispense with NSRange. Just iterate though all array elements, keep a counter up to the pre-calculate subarray size, and once it reaches the critical number, reset it to zero and start a new subarray to add to your result array.
This will presumably perform better if you use Swift arrays, but this is not strictly necessary.

Recursive Array Manipulation in Swift

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.

An Array of Doubles from Two Arrays of Strings

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.

Resources