Why does this simple array access not work in Swift? - arrays

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.

Related

Bit array from string

I've got a string:
cipher = "0111101110010111001001010000000110101000001000111101110000110101100100001100101100000"
I want to slice it and store in array like this:
["01111011", "10010111" ... ]
I tried this code, but i've got an error:
"cz.rb:16:in <main>': undefined methodpush' for nil:NilClass
(NoMethodError)"
i,j = 0,0
cipher_byte = []
while i < cipher.length
if i != 0 and i % 8 == 0
j+=1
end
cipher_byte[j].push(cipher[i])
p cipher_byte
i+=1
end
What's wrong with this?
It's ruby.
cipher.scan(/.{8}/)
#=> ["01111011", "10010111", "00100101", "00000001", "10101000",
# "00100011", "11011100", "00110101", "10010000", "11001011"]
Easiest:
cipher.each_char.each_slice(8).map(&:join)
Faster:
(0...cipher.length).step(8).map { |i| cipher[i, 8] }
Less code means less places an error could hide (as long the code is still readable). Ruby provides many idioms and methods to make things very intuitive and easy on the programmer. A while loop with a counter is rarely Rubyish; a while loop with an unconditionally incrementing counter never is.
You're indexing an empty array (cipher_byte) and getting nil. Then calling push on an instance of nil
You'll want to create a new array every etc iterations, then push that sub-array to the main array.
You are trying to push to cipher_byte[j], which, as the error is telling you, isn't set to a value yet. (On the first iteration of the loop, you can imagine that cipher_byte is of length 0, since it's set to []. Therefore, you cannot yet index into it using [j].)
You may want cipher_byte.push, rather than trying to push to a specific position. In Ruby, .push on an array will add that value to the end of the array; you don't need to reference that position with j.

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.

Recursive function not breaking

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.

Populating Array in Swift 4 with for-loop without using the index

I need to populate an Array (already declared and initialized) using a for loop in order to create a determinate amount of items.
I ended up with the following code:
func createValues() -> Array<Int> {
let usableRange:Range = 6..<11;
var arrayOfValues: Array<Int>=[]; //Array declared and initialized
for i in 0..<10 {
arrayOfValues.append(random(usableRange));
print(arrayOfValues[i]);
}
return arrayOfValues;
}
this code does what I expect it to do just fine. However, as soon as I comment out the line
print(arrayOfValues[i]);
Xcode throws the following warning:
Immutable value 'i' was never used; consider replacing with '_' or
removing it
If I accept the suggestion the code works, but not as fine as it did before.
I'm just transitioning from Obj-C to Swift and I don't really know what the proper way to do this should be. Any help would be appreciated. Thanks in advance.
P.S. I'm aware that I don't need semicolons anymore, but old habits die hard, I guess...
Since you don't use i, you can just write
for _ in 0 ..< 10
The _ means "yes, there is a value, but I don't care about it", here and in many other situations.
If you want just a good alternative for your code, I'm offering you this:
var i: Int = 0
while i < 10 {
arrayOfValues.append(random(usableRange))
i += 1
}
If the goal is to generate an array of random numbers in your given range, I would suggest you simply generate it directly. There is no need for the for loop.
let usableRange = UInt32(6)..<UInt32(11)
let arr = (0..<10).map { _ in Int(
arc4random_uniform(usableRange.upperBound - usableRange.lowerBound)
+ usableRange.lowerBound
)}
Array has a designated initalizer, which initalizes an array with a given size and a repeated value:
let values = Array(repeating: "VALUE", count: 5)
print(fiveZs)
// Prints "["VALUE", "VALUE", "VALUE", "VALUE", "VALUE"]"
Source: Apple Documentation

Using array values to init new array elements

I've put together the following sample code:
let tings = [1, 2, 4, 5, 6, 8]
class Foo {
var number: Int
init(something: Int) {
self.number = something
}
}
var list: [Foo] = []
for num in tings {
var temp = Foo(something: tings[num]) //excbadinstruction invop
list.append(Foo(something: 3)) //no error
list.append(Foo(something: Int(4))) //no error
list.append(Foo(something: tings[num])) //excbadinstruction invop
list.append(Foo(something: Int(tings[num]))) //excbadinstruction invop
}
I dont get what the problem is with the last two lines as in my opinion things[num] also is of type Int, and same of course with the last row.
If I specify the type of things to be [Int], nothing changes (which isnt strange to me)
I also have to explicitly state 'something:' which I didn't expect because it was the first and only argument of the initializer.
I hope anyone can explain this to me and/or give the right way to do this.
Swift Playground is fun and all, but it's pretty terrible when it comes to debugging.
If we duplicate this code in an actual compilable and build able application, we get no build time warnings or errors. However, as soon as we hit play and run the code, the problem is obvious. The console prints:
fatal error: Array index out of bounds
Here's the problem... our array has six elements. That means the maximum accessible index is 5.
Look at our loop:
for num in tings {
// do stuff
}
If we change the loop to print num, like this:
for num in tings {
println(num)
}
You'll see that it prints our tings array:
1
2
4
5
6
8
Now let's access the the element at each index:
for num in tings {
println(tings[num])
}
What prints?
2
4
6
8
fatal error: Array index out of range
What happened?
We tried to access the 6th index, because the fifth iteration of the loop, the fifth element of the array, is the Int 6. We're trying to access the 6th element, which is out of bounds for our array.
If you're trying to iterate of the elements of tings and instantiate a Foo for each element, you simply need this:
for num in tings {
list.append(Foo(something: num))
}
Or as Paul points out in a comment, if you need the index, you can iterate through the loop as such:
for (index, value) in enumerate(tings) {
// do stuff
}

Resources