Recursive function not breaking - arrays

I want to iterate over a 2D array and make subarrays whenever I find a different value in a specific column. Example:
TEST <----- This value should be ignored. Start counting at index 1.
A
A
A
-------- split here --------
B
B
B
-------- split here --------
C
-------- split here --------
This results in 3 arrays.
array1: [A,A,A]
array2: [B,B,B]
array3: [C]
My solution to this problem was a recursive method which takes the 2D array:
static func splitArray(fromArray array: [[String]], startIndex: Int = 1) {
for x in startIndex..<array.count {
if array.indices.contains(x+1) {
if (array[x][7]) != array[x+1][7] {
splitArray(fromArray: array, startIndex: x+1)
}
} else {
break
}
}
}
In this method I do the following:
Go through the array starting at index 1.
Compare the current index with the next index. If the next index has a different value split the array if not resume iterating.
To prevent array out of bounds I check if there's a next index - if there is no next index break the method (which should be called once the whole array has been iterated over)
Extra info:
The magic number 7 is the column in my 2D array I want to iterate over.
The method does reach the break command .. but somehow it jumps back in the method although it doesn't get called by the recursive splitArray call.
This method doesn't create the subarrays yet since the logic at this point is broken.
Why doesn't my function break? It does do its work - it splits correctly but then it starts over when it shouldn't.
P.S: If there's any coding advice I'd highly appreciate it, I feel this code in general is bad.

Solved it:
static func split(_ array: [[String]], startIndex: Int = 1) {
for x in startIndex..<array.count {
if array.indices.contains(x+1) {
if (array[x][7]) != array[x+1][7] {
split(array, startIndex: x+1)
break
}
}
}
}
The "fix" was to include the break after calling the recursive function. I guess the for loop resumed after calling split.

Related

Index out of range (fatal error) when summing diagonals in square arrays

I need difference between first and second diagonal in square matrix array
func diagonalDifference(arr: [[Int]]) -> Int {
var sumFirstDiag = 0
for i in 0...arr.count-1 {
sumFirstDiag = sumFirstDiag + Int(arr[i][i])
}
var sumSecondDiag = 0
for y in 0...arr.count-1 {
sumSecondDiag = sumSecondDiag + Int(arr[y][arr.count - y])
}
print(abs(sumFirstDiag - sumSecondDiag))
return abs(sumFirstDiag - sumSecondDiag)
}
let array = [
[1,2,3,11],
[4,5,6,12],
[7,8,9,90],
[1,3,5,7]]
diagonalDifference(arr: array)
Error message:
Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444
I figured I'd share a more functional approach to this same problem:
// The sum of elements along the diagonal from top-left to bottom-right
func diagonal1Sum(_ input: [[Int]]) -> Int {
input.indices
.map { input[$0][$0] }
.reduce(0, +) // I wish Swift had a built-in `.sum` :(
}
// The sum of elements along the diagonal from top-right to bottom-left
func diagonal2Sum(_ input: [[Int]]) -> Int {
zip(input.indices, input.indices.reversed())
.map { input[$0][$1] }
.reduce(0, +)
}
// The absolute value of the difference in sums of the two diagonals.
func diagonalDifference(_ input: [[Int]]) -> Int {
abs(diagonal1Sum(input) - diagonal2Sum(input))
}
let array = [
[1,2,3,11],
[4,5,6,12],
[7,8,9,90],
[1,3,5,7],
]
print(diagonalDifference(array))
There's a few things to notice:
0...arr.count-1 should be just expressed as arr.indices. The first form crashes on empty arrays. See https://github.com/amomchilov/Blog/blob/master/Proper%20Array%20Iteration.md
arr[i][i] is already an Int, there's no need to convert that into an Int.
sumSecondDiag = sumSecondDiag + something is more simply written as just sumSecondDiag += something
Meaningless parameter names like arr shouldn't be keyword labels. Compare diagonalDifference(arr: array) and diagonalDifference(array). The arr: in the first form doesn't really give you any information you didn't already have. You should omit that keyword label using a _.
You should not print the result out of a function that computes a result like this. Return the result, then print it. This gives users of your function the ability to choose whether they want to print, or not.
And most importantly: you can iterate through arr.indices.reversed() to get a sequence of indices that go from high to low, allowing you to access the row elements from top right to bottom left.
This is the key to avoiding the bug you encountered. You forgot the crucial - 1, which is why you were accessing the index out of bounds and causing the crash. If you just use the reversed reverse the array first, then access the nth element, you'll get back the n-th last value, without needing to remember to do acc.count - n - 1.
To pick the row, you still need the regular "forward indices", like array.indices().
You can use zip(_:_:) to iterate both of them at the same time, using the "forward index" to select the row, and the "backward index" to select a particular number from that row.
Thanks to #Rob for this suggestion.
Update notes
I revamped my recommendation. I was previously suggesting to use input[$0].reversed()[$0] under the misunderstanding that the result of reversed would be a view onto the array that would just perform constant-time index arithmetic. This is not the case. Each of these reversed calls was doing a full linear reversal of the array.

Is it safe to iterate an array while modifying it?

I know you shouldn't, I kind of know why. But I mean I don't understand my own code once I am trying really to think what's going on.
So I have an array with bunch of objects. I am iterating over it and once I find an object with specific type, I remove it from the array, and add another object into the array. So something like this:
var arr = parent.allchildren() //getting all the children in array
for ele in arr{
if(ele==somethingHere){
parent.remove(ele)
parent.add(new ele) //add new child into child array
}
}
If I have an array of 1,2,3,4,5, and I remove 3 and add a 6 while iterating, the actual array would be 1,2,4,5,6 but the array I am iterating would still be 1,2,3,4,5.
Which I think it would be fine, because at the end I still get what I want, which removed the element and added the element I need. However modifying the list while iterating it is bad and you shouldn't do that, but for my case I think it does what I need. What could be the potential issue in my case that I can't see?
One thing you may want to think about doing is making all of the changes at the end of the iteration. Instead of making the changes one by one, record the changes you want to make while iterating, and then actually make those changes once your loop is finished.
For example, you could make an array of elements to remove, and an array of elements to add.
//Our array where we record what we want to add
var elementsToAdd = [Any]()
//Our array of what elements we want to remove. We record the index at
//which we want to remove the element from the array
var indexesToRemoveAt = [Int]()
//Getting all the children in array
var arr = parent.allchildren()
//Enumerating an array allows us to access the index at which that
//element occurs. For example, the first element's index would be 0,
//the second element's index would be 1, the third would be 2, and so
//on
for (index,ele) in arr.enumerated() {
if(ele == somethingHere) {
indexesToRemoveAt.append(index)
elementsToAdd.append(newEle)
}
}
//Now that we have recorded the changes we want to make, we could make
//all of the changes at once
arr.remove(at: indexesToRemoveAt)
arr.append(contentsOf: elementsToAdd)
Note that removing array elements at multiple indexes would require the following extension to Array. If you wanted to avoid creating this extension, you could always just loop through the array of indexes and tell the array to remove at each individual index. All this extension function is really doing is looping through the indexes, and removing the array element at said index.
Array extension to remove elements at multiple indexes:
extension Array {
//Allows us to remove at multiple indexes instead of just one
mutating func remove(at indexes: [Int]) {
for index in indexes.sorted(by: >) {
if index <= count-1 {
remove(at: index)
}
}
}
}
I just tested in a playground with the following code:
var arr = ["hi", "bye", "guy", "fry", "sky"]
for a in arr {
if arr.count >= 3 {
arr.remove(at: 2)
}
print(a)
}
print(arr)
This prints:
hi
bye
guy
fry
sky
["hi", "bye"]
So it looks like when you use a for-in loop in Swift, the array is copied and changes you make to it will not affect the array you are iterating over. To answer your question, as long as you understand that this is the behavior, there's nothing wrong with doing this.

Swift algorithm to enumerate a multilinear map, using multiples indexes:[Int]

A multilinear map M has its elements stored in a one-dimension array of length N, with a Shape S defined by S:[Int] = [p,q,r,...] so that q*p*r*... = N. The Shape is of variable size, not known at compile time.
The issue I'm trying to solve is a generic approach to accessing the map's elements using an array of integers, which individual values are coordinates in the Shape S, ex: M[1,3,2], M[2,3,3,3] etc... This is a problem different from a simple enumeration of the map's elements.
One method is to use M[i,j,k] and implement a subscript method. Unfortunately, this approach hardcodes the map's shape, and the algorithm is no longer generic.
Say there's a utility function that returns an element index from a tuple derived from the map's Shape, so that:
func index(_ indexes:[Int]) -> Int {....}
func elementAt(indexes:[Int]) -> Element {
return elements_of_the_map[self.index(indexes)]
}
M.elementAt(indexes:[i,j,k]) or M.elementAt(indexes:[i,j,k,l,m]) always work. So the problem at this point is to build the array [i,j,k,...]
Question: Is there an algorithm to efficiently enumerate those indexes? Nested loops won't work since the number of loops isn't known at compile time, and recursive function seem to add a lot of complexity (in particular keeping track of previous indexes).
I'm thinking about an algorithm 'a la' base-x counting, that is adding one unit to the top right index, and moving leftwards one unit if the count exceeds the number of elements by the map's Shape.
Same idea, but less code:
func addOneUnit(shape: [Int], indexes: [Int]) -> [Int]? {
var next = indexes
for i in shape.indices.reversed() {
next[i] += 1
if next[i] < shape[i] {
return next
}
next[i] = 0
}
return nil
}
Here's the code, it's primitive, but should work. The idea is to increment, right-to-left, to move say to [1,2,2] from [1,2,1] with the shape constraint [2,3,3].
func add_one_unit(shape:[Int],indexes:[Int]) -> [Int]? {
//Addition is right to left, so we have to reverse the arrays. Shape Arrays are usually very small, so it's fast.
let uu = Array(indexes.reversed()); //Array to add one index to.
let shape_reversed = Array(shape.dimensions.reversed()); //Shape array.
var vv:[Int] = [];
var move_next:Bool = true;
for i in 0..<uu.count {
if move_next {
if uu[i] < shape_reversed[i] - 1 { //Shape constraint is OK.
vv.append(uu[i] + 1)
move_next = false;
} else {
vv.append(0) //Shape constraint is reached.
move_next = true;//we'll flip the next index.
}
} else {
vv.append(uu[i]) //Nothing to change.
}
}
return ( vv.reduce(true, { $0&&($1 == 0) }) ) ? nil : Array(vv.reversed()); //Returns nil once we reached the Zero Vector.
}
Which gives
add_one_unit(shape:[2,3,3],indexes:[0,0,0]) -> [0,0,1]
add_one_unit(shape:[2,3,3],indexes:[1,2,2]) -> [0,0,0]/nil
Once this is done, this function can be used to enumerate a multilinear map of any shape (a mapping of [i,j,k,...] to a unique index such as matrix to index mapping is necessary and depends on your implementation), or slice a map starting from any particular vector.

Why does this simple array access not work in Swift?

var word = "morning"
var arr = Array(word)
for s in 0...word.count {
print(arr[s])
}
This will not print. Of course, if I substitute a number for s, the code works fine.
Why will it not accept a variable in the array access braces? Is this peculiar to Swift?
I've spent a long time trying to figure this out, and it's nothing to do with s being optional.
Anyone understand this?
you are using inclusive range ... instead of ..<, so s goes from 0 to 7, not 0 to 6.
However, in arr the index goes from 0 to 6 because there are 7 characters.
Thus, when the program tries to access arr[7], it throws an index out of range error.
If you were coding on Xcode, the debugger would have told you that there is no arr[7].
As for the code, here is a better way to print every item in arr than using an index counter:
var word = "morning"
var arr = Array(word)
for s in arr {
print(s)
}
This is called a "foreach loop", for each item in arr, it assigns it to s, performs the code in the loop, and moves on to the next item, assigns it to s, and so on.
When you have to access every element in an array or a collection, foreach loop is generally considered to be a more elegant way to do so, unless you need to store the index of a certain item during the loop, in which case the only option is the range-based for loop (which you are using).
Happy coding!
When I run it, it prints the array then throws the error Fatal error: Index out of range. To fix this, change the for loop to:
for s in 0..<word.count {
print(arr[s])
}
try this
when you use a word to recognize
size of Array your Array index start as 0 so array last index must be equal with your (word.count - 1)
var word = "morning"
var arr = Array(word)
for s in 0...(word.count-1) {
print(arr[s])
}
Basically avoid index based for loops as much as possible.
To print each character of a string simply use
var word = "morning"
for s in word { // in Swift 3 word.characters
print(s)
}
To solve the index issue you have to use the half-open range operator ..< since indexes are zero-based.

Swift Dictionary of Arrays

I am making an app that has different game modes, and each game mode has a few scores. I am trying to store all the scores in a dictionary of arrays, where the dictionary's key is a game's id (a String), and the associated array has the list of scores for that game mode. But when I try to initialize the arrays' values to random values, Swift breaks, giving me the error below. This chunk of code will break in a playground. What am I doing wrong?
let modes = ["mode1", "mode2", "mode3"]
var dict = Dictionary<String, [Int]>()
for mode in modes
{
dict[mode] = Array<Int>()
for j in 1...5
{
dict[mode]?.append(j)
let array:[Int] = dict[mode]!
let value:Int = array[j] //breaks here
}
}
ERROR:
Execution was interrupted, reason: EXC_BAD_INSTRUCTION(code=EXC_I386_INVOP, subcode=0x0).
Your problem is array subscripts are zero-based. So when you write:
var a: [Int] = []
for i in 1...5 {
a.append(42)
println(a[i])
}
you will get a runtime error, because first time around the loop you are subscripting a[1] when there is only an a[0]. In your code, you either need to do for j in 0..<5 or let value = array[j-1].
By the way, even though it’s perfectly safe to do dict[mode]! (since you just added it), it’s a habit best avoided as one of these days your code won’t be as correct as you think, and that ! will explode in your face. There’s almost always a better way to write what you want without needing !.
Also, generally speaking, whenever you use array subscripts you are risking an accidental screw-up by accidentally addressing an out-of-bounds index like here. There are lots of alternatives that mean actually using a[i] is easy to avoid:
If you want the indices for a collection (like an array), instead of:
for i in 0..<a.count { }
you can write
for i in indices(a) { }
If you want to number the elements in an array, instead of
for i in indices(a) { println("item \(i) is \(a[i])" }
you can write
for (i, elem) in enumerate(a) { println("item \(i) is \(elem)") }
If the collection happens to have an Int for an index (such as Array), you can use i as an index, but if it doesn’t (such as String) an alternative to get the index and element is:
let s = "hello"
for (idx, char) in Zip2(indices(s),s) { }
If you want the first or last element of an array, instead of:
if a.count > 0 { let x = a[0] }
if a.count > 0 { let x = a[a.count - 1] }
you can write
if let first = a.first { let x = first }
if let last = a.last { let x = first }
Prefer map, filter and reduce to for loops in general (but don’t obsess over it, sometimes a for loop is better)

Resources