Swift: Working with for and array - swift

I started to learn Swift but I have some problems like I want to sum two arrays and put them in a third array and I have to do it with for loop.
var ar1 = [1,3,5,7,9]
var ar2 = [2,4,6,8,10]
var ar3 : [Int] = [5]
for i in 0... ar1.count-1 {
// for loop for index
ar3[i] = ar1[i] + ar2[i]
}
But I get an out of range error. I tried a lot but I could not solve it.

Your code looks more like C or Java than swift, but you're on the right track. There are many other ways to do this, but this code is very similar to what you were trying to do:
func example() {
let array1 = [1, 3, 5, 7, 9]
let array2 = [2, 4, 6, 8, 10]
var array3 = [Int]()
for i in 0 ..< array1.count {
array3.append(array1[i] + array2[i])
}
for element in array3 {
print("\(element)")
}
}
Use "let" if the variable never changes once you defined it.
I added a loop at the bottom so you can verify the results, and also so you can see that you don't need "i" in loops.

Related

How to create a new array of even numbers from an existing array?

I am trying to create a function that takes an array of Int, and returns a new array of all of the even numbers in the original array.
I have been fumbling around with this code (I am a very new beginner)
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray: [Int] = []
func newInt(newEven: Int, newArray: [Int]) -> Int {
for newEven in numberArray{
var index = 0
index += 1
if newEven % 2 == 0 {
newArray.insert(newEven, at:[index])
}
return newEven
}
}
print(newArray)
This is a good start! Here are some pointers:
1. Formatting
The formatting needs some work. Generally, every new scope ({ ... }) should introduce a new layer of indentation, like so:
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray: [Int] = []
func newInt(newEven: Int, newArray: [Int]) -> Int {
for newEven in numberArray{
var index = 0
index += 1
if newEven % 2 == 0 {
newArray.insert(newEven, at:[index])
}
return newEven
}
}
print(newArray)
Now we can make some observations:
1. index is scoped to the for loop body, and will always have the same value of 0, and then 1 after the next line.
2. The return statement is within the for loop body, and unconditional. This function will always return the value of the first element of numberArray
3. The return type of this function is Int. But in your question, you state that you want this to return an array of all of the even numbers. So your return type will have to be Array<Int> (a.k.a. [Int]), not just Int.
2. Compilation issues
This function has several errors that will prevent compilation:
The return statement is within a loop body. If numberArray is empty, and the for loop body is never entered, then you don't hit the return statement. Once control reaches the end of the function, what value should be returned? It's not defined, so that's an error. In Swift, all code paths through a function must return a value. (with the exception of Void functions, which implicitly return nothing at the end)
You're trying to call Array.insert(_:at:) with a second argument of [index], which is an array literal of type Array<Int>. It should just be index.
3. Logical issues
Your function introduces a parameter called newArray, which shadows the global variable newArray on the line before it. The global variable isn't necessary, and you should delete it.
Your function operates over numberArray, but doesn't explicitly take it as input via a parameter. Rather than hardcoding a reference to a global variable like numberArray, you should use a parameter.
The parameter newEven is unused, and is shadowed by the local variable of the for loop
Your function name newInt(newEven:newArray:) doesn't describe what the function does. Consider a function signature like func allEvens(in input: [Int]) -> [Int]
You never actually call this function. You declare it, but never told the program to run it.
You don't need to use Array.insert(_:at:). You can simply use Array.append, which will automatically append elements to the end of the array.
4. Recommendations
Fix the method signature. You want the function to take some numbers, and output only the even numbers. Model that in code: func allEvens(in input: [Int]) -> [Int]
Create a new empty array locally (within the function), into which the even numbers will be stored. As you loop over the input array, check every number if it's even, and if so, append it to the evens array.
Return the evens array.
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray = numberArray.filter({$0 % 2 == 0})
This should return a new array with even numbers.
As LeoDabus mentioned in his comment, the functionality you're seeking is already contained within Swift's Standard Library, so it's not really necessary to write a dedicated function to accomplish that task. Here's how you would do it:
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let newArray = numberArray.filter { $0.isMultiple(of: 2) }
print(newArray) // [2, 4, 6, 8, 10]
In this, you're using the filter whether $0 (where $0 is an element in your array) is a multiple of the number you specified in the parameter, in this case, 2.
As you see in the documentation, isMultiple(of:) returns a Bool (true or false). This is the signature:
func isMultiple(of other: Self) -> Bool
I would recommend having a peek at this post covering the topics of map, filter, and reduce. These are useful things to know when starting out in Swift.
Additionally, I've found Dash to be extremely helpful in navigating documetation for Swift.
Update
I should have read your question more thoroughly, as I missed the part where you've gotta do it with a loop. Swift has a cool method called forEach, which I'm a huge fan of. Using that methodology, it would look something like this:
func filter(array: [Int], forMultiplesOf multiple: Int) -> [Int] {
// Create a landing spot for whatever is a multiple...it starts as empty
var newArray: [Int] = []
// This is not the most compact way, but it satisfies the loop constraint
array.forEach { number in
if number % multiple == 0 {
newArray.append(number)
}
}
// Once you're done with the loop, then return the new array you declared at the beginning
return newArray
}
And you'd call it like so:
let newArrayUsingFunction = filter(array: numberArray, forMultiplesOf: 2)
What you're doing here is passing in 2 parameters to the function (array & multiple) to return an array of Ints. See comments in code for what's going on

How to transpose an array more Swiftly?

I want to transpose an array of arrays like this:
Original array:
[
[1,2,3],
[4,5,6],
[7,8,9]
]
Result:
[
1,4,7,
2,5,8,
3,6,9
]
[1,4,7,2,5,8,3,6,9]
Assume all the subarrays have the same length.
If you haven't noticed already, the first three items in the result is the first item of the three subarrays. The fourth, fifth and sixth items in the result is the second item of each subarray.
If you still don't understand, maybe this will help:
At the moment, I have this:
func flatten(array: [[Int]]) -> [Int] {
var flat = [Int]()
for i in 0..<array[0].count {
for subarray in array {
flat.append(subarray[i])
}
}
return flat
}
I don't think that is very swfity. How can I do this in a swifty way?
Just to avoid being an XY problem, here's why I want to do this.
I am developing a board game. I am using HLGridNode (It's basically a bunch of squares in a grid-like layout) from HLSpriteKit as the board game's board. To edit the contents of the grid node, I need to pass in an 1D array of sprite nodes, not a 2D array.
To make my life easier, I stored the model objects in a 2D array. This way, I can refer to the sqaure 5 squares from the left and 2 squares from the top just by doing:
modelObjects[5][2]
If I flatten the 2D array using .flatMap { $0 } and pass the result to the grid node, modelObjects[5][2] would appear to be 2 squares from the left and 5 squares from the top.
This is not a duplicate of this because that question seems to have definite number of arrays to work with. Although I can put my 2D array into a loop, and do those enumerate().map {...} stuff, it seems like a really long-winded approach. I think there must be a simpler to do this with 2D arrays.
Here's an improvement on Shadow Of's answer:
extension Collection where Self.Iterator.Element: RandomAccessCollection {
// PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
func transposed() -> [[Self.Iterator.Element.Iterator.Element]] {
guard let firstRow = self.first else { return [] }
return firstRow.indices.map { index in
self.map{ $0[index] }
}
}
}
let matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
]
matrix.transposed().forEach{ print($0) }
You can receive result you wanted by transpose your 2d matrix, using, for example, this function:
func matrixTranspose<T>(_ matrix: [[T]]) -> [[T]] {
if matrix.isEmpty {return matrix}
var result = [[T]]()
for index in 0..<matrix.first!.count {
result.append(matrix.map{$0[index]})
}
return result
}
and applying flatten (joined in swift 3) then.
let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(matrixTranspose(arr))
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
print(matrixTranspose(arr).flatMap{$0})
// [1, 4, 7, 2, 5, 8, 3, 6, 9]
Extension version:
extension Collection where Self.Iterator.Element: Collection {
var transpose: Array<Array<Self.Iterator.Element.Iterator.Element>> {
var result = Array<Array<Self.Iterator.Element.Iterator.Element>>()
if self.isEmpty {return result}
var index = self.first!.startIndex
while index != self.first!.endIndex {
var subresult = Array<Self.Iterator.Element.Iterator.Element>()
for subarray in self {
subresult.append(subarray[index])
}
result.append(subresult)
index = self.first!.index(after: index)
}
return result
}
}
with usage
let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(arr.transpose)
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Index out of range in Swift with removeAtIndex

I tried to remove an element in NSUserDefaults which is the same as stockSymbol's value when click a button. My idea is that cast the NSUserDefaults to an array and remove the element with removeAtIndex. Here is my code.
#IBAction func buttonFilledStarClicked(sender: AnyObject) {
NSLog("Filled star clicked")
self.buttonFilledStar.hidden = true
self.buttonEmptyStar.hidden = false
var Array = NSUserDefaults.standardUserDefaults().objectForKey("favorites")! as! [String]
var countArray = (NSUserDefaults.standardUserDefaults().objectForKey("favorites")! as! [String]).count - 1
for i in 0...countArray {
if stockSymbol! == Array[i] {
NSLog("i is : \(i)")
Array.removeAtIndex(i)
}
else {}
}
NSLog("Array is: \(Array), countArray is: \(countArray)")
}
However it has 'out of index' error.
It works when I just comment Array.removeAtIndex(i) out.
Array looks like this --
["aa", "bb", "Test!", "Test!"]
Any suggestions? Thank you in advance.
So, the change you can make to resolve the error with the least impact on your code overall would be to simply iterate through the indices backwards:
for i in (0...countArray).reverse() {
if stockSymbol! == Array[i] {
NSLog("i is : \(i)")
Array.removeAtIndex(i)
}
else {}
}
But the best option is to just use Swift's filter:
Array = Array.filter { $0 != stockSymbol }
An expanded note on why the crash is happening...
Let's take a simplified example. Say I have the following array:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
And I want to remove all of the odd numbers out of it. Using your first naïve approach, I might write my logic like this:
for i in 0..<arr.count {
if arr[i] % 2 != 0 {
arr.removeAtIndex(i)
}
}
Look at what happens on each iteration.
On the first iteration, we have arr[i] of 1. This is an odd number, so we'll removeAtIndex, and our array now actually looks like this:
[2, 3, 4, 5, 6, 7, 8, 9, 10]
The array's size is now smaller--it has just 9 elements. But the loop doesn't work like an old C-style for loop where i < arr.count is checked on each iteration (which is part of why this loop is faster).
But notice something else that happens when we iterate forward...
On the second iteration, i is equal to 1, and so what does arr[i] give us? It gives us 3. We never even check 2. On the first iteration, when i was 0, it was at index 1. On the second iteration, when i is 1, the 2 is at index 0.
So on the second iteration, we'll call removeAtIndex with i equal to 1 and remove the 3.
This pattern will continue for a few iterations until we end up with our array of just even numbers:
[2, 4, 6, 8, 10]
But this happens after the iteration where i was equal to 4, and the loop is going to try running until i is equal to 10.
On the sixth iteration of the loop, we try to access the element at index 5 of the array. But the array only has five elements, so the largest index is 4. When we try to access index 5, we crash.
You should not remove it from inside of the loop, when you call removeAtIndex(i) Array removes the item so you have 1 less item then countArray.. so you have to have another array to remember which item you want to remove and remove it outside of the loop.. or better option is to use filter
// Filter only strings that match stockSymbol
Array = Array.filter { $0 == stockSymbol! }
Try to find out which i that give you index out of range result. Is it the 0 or the last one. Maybe you will find some other useful clue.
First of all Array is a type, maybe you can call:
var favourites = NSUserDefaults.standardUserDefaults().objectForKey("favorites")! as! [String]
Then you don't need a array's count variable you can access using count arrays property.
Finally if you are iterating through an array and remove an element it always going to throw "index's error" because the index is not the same as the beginning..
For solving this you can take two pointers of the index variable, but what I would do is something like this:
var correctElements = favourites.filter({$0!=stockSymbol})

Reduce to count elements in a dictionary in Swift

I'm trying to count the elements of my dictionary. The dictionary is of type [EKCalendar: ReminderList] where ReminderList is a class with a list property. I want to go through the dictionary and add up the count of all these lists.
My dictionary is in the property self?.reminderListsStructure.structure.
let numberOfElements = self?.reminderListsStructure.structure.reduce(0) {
accumulator, nextValue in
return accumulator.list.count + nextValue.list.count
// COMPILE ERROR: Type of expression is ambiguous without more context
}
let count = reminderListsStructure.structure.reduce(0) { $0 + $1.1.list.count }
Something like this. Not enough info though, so I'm not 100% sure it works.
I think flatMap is a more appropriate choice here:
let input = [
1: [1],
2: [1, 2],
3: [1, 2, 3],
4: [1, 2, 3, 4],
5: [1, 2, 3, 4, 5]
]
let output = input.values.flatMap{$0}.count //15
When you reduce a dictionary, the element type is a tuple of the Key and Value types, so you can use:
dictionary.reduce(0) { $0 + $1.1.list.count }
Or, you can get just the values from the dictionary and reduce that:
dictionary.values.reduce(0) { $0 + $1.list.count }
Note that since Dictionary.values returns a lazy iterator, there's not really a huge cost to using it.
More simple and clear way goes like this,
var dict = ["x" : 1 , "y" : 2, "z" : 3]
let count = dict.reduce(0, { x, element in
//maybe here some condition
//if(element.value > 1){return x}
return x + 1
})

How to add integer to array (with explicite int index) in swift?

I read swift handbook and was trying to do some exercises. But I run into a problem and I do not know if I do something wrong or if xCode 6 beta is just buggy.
// Playground - noun: a place where people can play
import Cocoa
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var lastLargest = Integer[]()
var index = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
//lastLargest[index] = number
index++
largest = number
}
}
}
index
lastLargest
largest
As soon as I uncomment lastLargest[index] = number I do not get any results on right side in playground. Nor I get any infos about index, lastLargest or largest.
Following example does not work either:
var index2 = 0
var lastLargest2 = Integer[]()
lastLargest2[index2] = 1
index2++
lastLargest2[index2] = 2
You are appending using an out of bound array-index. Don't do that. Instead, use append:
lastLargest.append(number)
From Apple's documentation:
You can’t use subscript syntax to append a new item to the end of an array. If you try to use subscript syntax to retrieve or set a value for an index that is outside of an array’s existing bounds, you will trigger a runtime error.
When you're using explicit indexes (subscript notation) to set values in a mutable array, some value must already exist in that array at that index. When you use subscript notation, you're essentially using a 'set', rather than a 'set and add if necessary'.
As a result, you should be using:
lastLargest.insert(number, atIndex: index)
If you want to insert a new item. This will let you insert an item at the specified index, assuming your collection's size is already greater than or equal to the index you're trying to replace.