Swift: Index out of range (for) - swift

I am trying to do challange, but i stumbled on error, that my index is out of range. I do not know what can be the problem.
I tried anything that came to my mind. Thanks for any help.
func countApplesAndOranges(s: Int, t: Int, a: Int, b: Int, apples: [Int], oranges: [Int]) -> Void {
var positionApples : [Int] = apples
var positionOranges : [Int] = oranges
for i in positionApples {
positionApples[i] += a //This line
}
for i in positionOranges {
positionOranges[i] += b //This line
}
var hitApples : Int = 0
var hitOranges : Int = 0
for i in positionApples {
if i >= s && i <= t {
hitApples += 1
}
}
for i in positionOranges {
if i >= s && i <= t {
hitOranges += 1
}
}
print(hitApples)
print(hitOranges)
}
Array apples and oranges should copy to positionApples and positionOranges. Then to each item in the array, value should be added, but that just throws runtime error.

Assuming the value of apples is [2, 4, 6, 8] then the code
for i in positionApples {
print(i)
}
prints the elements 2, 4, 6, 8 and not the indices 0, 1, 2, 3 so in the second iteration of
for i in positionApples {
positionApples[i] += a //This line
}
i is 4 but the end index is actually 3 and the code will 🧨.
If you need the loop index enumerate the array
for (index, element) in positionApples.enumerated() {
print(index, element)
}

Let say, we call this method as shown below.
countApplesAndOranges(s: 3, t: 3, a: 3, b: 3, apples: [1,2,3], oranges: [4,5,6])
In the below code, possible value of i in the for loop is 1,2,3. But the actual indices of positionApples can be 0,1,2 only. When it tries to access array of index=3, it throws
"Fatal error: Index out of range"
for i in positionApples {
positionApples[i] += a //This line
}

I think this is what you wanted to do.
for i in 0 ..< positionApples.count {
positionApples[i] += a
}
This way, you can modify each element in the array.

Related

I need to compare 3 elements in a row in an array, but i get index out of range error

The problem: to return a number of zeros in an array that contains 0s and 1s, if there are 3 0s in a row, count them as one, for example [0, 1, 0, 0, 0, 1, 0, 1, 0] should return 4, but when i try to solve it like this
func findZeros(_ c: [Int]) -> Int {
var zeros = 0
for var i in 0..<c.count {
switch c[i] {
case _ where c[i] == 0 && c[i+1] == 0 && c[i+2] == 0: // row 5
zeros += 1
i += 2
case _ where c[i] == 0:
zeros += 1
default:
break
}
}
return zeros
}
i always get index out of range error in row 5 , although when i hardcode c[1], c[2], c[3] == 0, it just counts as false and goes through... i've just started to learn swift so maybe that's not optimal, but anyway i can't even get this one working :/
You can group the consecutive elements and sum how many groups you have of those elements:
extension Collection where Element: Equatable {
var grouped: [[Element]] {
reduce(into: []) {
// check if the last element of the last collection is equal to the current element
$0.last?.last == $1 ?
// append the element to the last collection
$0[$0.index(before: $0.endIndex)].append($1) :
// otherwise add a new collection with the new element
$0.append([$1])
}
}
func repeatedOccurences(of element: Element) -> Int {
// if the collection first element is equal to the element add one otherwise return the current result
grouped.reduce(0) { $1.first == element ? $0 + 1 : $0 }
}
}
[0, 1, 0, 0, 0, 1, 0, 1, 0].repeatedOccurences(of: 0) // 4
[0, 1, 0, 0, 0, 1, 0, 1, 0].repeatedOccurences(of: 1) // 3
If you were only accessing c[i], then counting i up to c.count - 1 would be fine - but you're also trying to access c[i+1] and c[i+2], so you need to take that into account when setting the upper boundary of the range - and then verify that the array indeed has at least 3 elements:
if c.count < 3 {
return 0;
}
for var i in 0..<c.count-2 {
// now you can safely access c[i+2]
}
I'm going to show a very different solution approach. It's not one that would come naturally to a new beginner, so don't worry if it's foreign to you. It's something that you might come up with from having a bit more experience and being able to relate scattered concepts with each other.
The over-all process is actually very straight-forward, and is an almost exact codification of the your english explanation to the solution:
Identify all the sub-sequences of repeating elements. These are called "runs", and there's a concept called a "run-length encoding". It takes an input like ["A", "B, B", "C", "D", "D", "D"], and turns it into a sequence like [("A", 1), ("B", 2), ("C", 1), ("D", 3)]
Identify the runs of 0s that repeat precisely 3 times, and treat them as runs of a single 0.
Count the number of 0s in the runs.
Here's what the code to do that would look like:
let input = [0, 1, 0, 0, 0, 1, 0, 1, 0]
let result = input.runLengthEncoded()
.lazy
.filter(keepOnlyRunsOfZeros)
.map(convertThreeCountRunsIntoOneCountRuns)
.map { $0.count }
.reduce(0, +)
print(result)
And here are the supporting functions that make it possible:
typealias Run = (element: Int, count: Int)
func keepOnlyRunsOfZeros(_ run: Run) -> Bool {
return run.element == 0
}
func convertThreeCountRunsIntoOneCountRuns(_ run: Run) -> Run {
if run.count == 3 {
return (element: run.element, count: 1)
}
else {
return run
}
}
Knowing that the process in #1 is a known algorithm called run-length encoding, I can find and reuse an existing implementation. Over time as a developer, you build up a collection of useful functions/techniques that you reuse for future use. Often times, you find other people's libraries that you've come to find useful, thus have booked-marked and re-use.
In this case, I have an implementation of run-length encoding that I've written and used in previous projects. It's quite long, but it's generalized and lazy-evaluated (it doesn't need to make an array of all runs, it serves them one by one as you request them, which improves performance for huge inputs), which isn't strictly necessary in this case, but it's what I already have on hand.
There alternative implementations that you can find that are eagerly evaluated and less generic (which should be fine for a simple problem like this), feel free to substitute one of those, instead.
public extension Sequence where Self.Iterator.Element: Equatable {
func runLengthEncoded() -> LazySequenceRunLengthEncoder<Self> {
return LazySequenceRunLengthEncoder(encoding: self)
}
}
public struct LazySequenceRunLengthEncoder<WrappedSequence: Sequence>: Sequence
where WrappedSequence.Element: Equatable {
public let wrappedSequence: WrappedSequence
public init(encoding wrappedSequence: WrappedSequence) {
self.wrappedSequence = wrappedSequence
}
public func makeIterator() -> RunLengthEncodingIterator<WrappedSequence.Iterator> {
return RunLengthEncodingIterator(encoding: wrappedSequence.makeIterator())
}
}
public struct RunLengthEncodingIterator<WrappedIterator: IteratorProtocol>: IteratorProtocol
where WrappedIterator.Element: Equatable {
public private(set) var wrappedIterator: WrappedIterator
public private(set) var currentGrouping: (element: WrappedIterator.Element, count: Int)? = nil
public init(encoding wrappedIterator: WrappedIterator) {
self.wrappedIterator = wrappedIterator
}
public mutating func next() -> (element: WrappedIterator.Element, count: Int)? {
while let newElement = wrappedIterator.next() { // Take all elements of this run
if let currentGrouping = self.currentGrouping {
if newElement == currentGrouping.element { // increment the current run
let newCount = currentGrouping.count + 1
self.currentGrouping = (element: newElement, count: newCount)
} else { // Broke the streak
defer {
self.currentGrouping = (element: newElement, count: 1) // start a new group
}
return self.currentGrouping
}
} else { // There is no current group, this is the first element
self.currentGrouping = (element: newElement, count: 1)
}
}
// Reached end of the wrapped iterator
// 2. Only return the current grouping once, return the `nil` next time to end this iterator.
defer { self.currentGrouping = nil }
// 1. Return current grouping, if there is one
return self.currentGrouping
}
}

TwoSum Swift Solution

I just started learning coding with swift, and was trying TwoSum.
"Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1]."
I found some solutions from GitHub that I cannot understand.
code is from https://github.com/soapyigu/LeetCode-Swift/blob/master/Array/TwoSum.swift
class TwoSum {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict = [Int: Int]()
for (i, num) in nums.enumerated() {
if let lastIndex = dict[target - num] {
return [lastIndex, i]
}
dict[num] = i
}
fatalError("No valid outputs")
}
}
Could someone be so kind to explain to codes. Thanks a lot.
The dict initialised in the method stores the numbers in the input as keys, and their indices as values. The program uses this to remember which number is where. The dict can tell you things like "the number 2 is at index 0".
For each number num at index i in the input array, we subtract num from the target to find the other number that we need, in order for them to add up to target.
Now we have the other number we need, we check to see if we have seen such a number before, by searching dict. This is what the if let lastIndex = dict[target - num] part is doing. If the dict knows what index the other number is at, we return that index, and i.
If we haven't seen that number before, we record i into the dictionary under the key num, hoping that in later iterations, we can find a number that when added to num, makes 9.
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
var arr:[Int] = []
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var toggle = false
for i in 0..<nums.count {
for j in i+1..<nums.count {
if toggle == false {
if(nums[i]+nums[j]==target){
toggle = true
arr.insert(i, at: 0)
arr.insert(j, at: 1)
break
}
}
}
}
return arr
}
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
In Sweeper's excellent answer, he explained what dict is used for: It lets you use a value from the array to find that value's index. It would be more obvious what the dictionary was used for if we called it indexes, and this code builds the same dictionary in a more explicit way:
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
After that, you get a dictionary:
[2:0, 7:1, 11:2, 15:3]
You could write the function this way:
func twoSum(_ array: [Int], _ target: Int) -> [Int] {
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
for index in 0..<array.count {
let value = array[index]
if let otherIndex = indexes[target - value],
index != otherIndex {
return [index, otherIndex]
}
}
fatalError("Unable to match values")
}
That is a much more long-winded (and less efficient) way of doing the same thing. It loops through the array twice instead of once, but the results should be the same.
func twoSum(array: [Int], target: Int) -> [Int] {
var dict = [Int:Int]()
for (index, number) in array.enumerated() {
let value = target - number
if let sum = dict[value] {
return [sum, index]
}
dict[number] = index
}
return [0,0]
}
/*
array=[1, 2, 3] -> target=4
enumerated() => [0,1], [1,2], [2,3]
(i, n)
v4 - 1 = 3
s[3:0]
s[3:0]
v4 - 2 = 2
s[2:0]
s[2:1]
v4 - 3 = 1
s[1:1]
s[1:2]
output [0,2]
*/
var numbers: [Int] = [1, 3, 6, 7, 7, 14, 12]
var target = 26
var result = [Int]()
for i in 0..<numbers.count {
for j in i+1..<numbers.count {
if numbers[i] + numbers[j] == target {
print(numbers[i],numbers[j])
result.append(i)
result.append(j)
}
}
}
print(Array(Set(result)))
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict:[Int:Int] = [:]
for i in 0..<nums.count {
if dict[target - nums[i]] != nil {
return [dict[target - nums[i]] ?? 0, i]
} else {
dict[nums[i]] = i
}
}
return [0]
}
Here is a link to the discussion section of the TwoSum problem on Leetcode.
Lots of great Swift solutions there.
https://leetcode.com/problems/two-sum/discuss/?currentPage=1&orderBy=most_votes&query=swift.
My personal two cents -
func twoSumA(_ nums: [Int], _ target: Int) -> [Int] {
var numsHashMap: Dictionary<Int, Int> = [:]
var outputArr: [Int] = []
for index in 0..<nums.count {
let currentNum = nums[index]
if numsHashMap.keys.contains(target-currentNum) {
outputArr.append(numsHashMap[target-currentNum] ?? -1)
outputArr.append(index)
return outputArr
}
numsHashMap[currentNum] = index
}
return !outputArr.isEmpty ? outputArr : [-1, -1]
}

Factorial with intermediate results - Swift playgrounds - index out of range error

Getting blind from looking at this for too long. Can't spot the error.
Getting the "index out of range" error when calling the function factorialIntermediateResults(n: 4) Hope somebody can take a look with fresh eyes and help me spot the error. Thanks!
func factorialIntermediateResults(n: Int) -> [Int] {
if n == 0 || n == 1 { return [1] }
var results = [Int]()
doAllFactorials(n, &results, 0)
return results
}
func doAllFactorials(_ n: Int, _ results: inout [Int], _ level: Int) -> Int {
if n > 1 {
results[level] = n * doAllFactorials(n-1, &results, level+1)
return results[level]
} else {
results[level] = 1
return 1
}
}
factorialIntermediateResults(n: 4)
results is an empty array but you try to access values without appending values first.
The simplest solution might be to pre-populate your array with zeros.
var results: [Int] = Array(repeating: 0, count: n)

Group dictionary by key in Swift

I'm trying to implement a groupBy functionality where all the numbers of a nested list are grouped. My code so far:
struct MyClass {
var numbers: [Int]
...
}
var dict: [String : MyClass] = ...
let numbers = dict
.filter{ $0.0.containsString(searchString) }
.flatMap{ $0.1.numbers }
This yields me an Array of Ints. However I'd like to have a dictionary [Int : Int] with each unique number and the count of its occurence. So for example:
[1,2,3,4,1,2,2,1]
should be:
[1 : 2, 2 : 3, 3 : 1, 4 : 1]
I know there's a groupBy operator, but Swift doesn't seem to have one. I've tried with reduce:
func reducer(accumulator: [Int: Int], num: Int) -> [Int : Int] {
var acc = accumulator
acc[num]! += 1
return acc
}
filtered.reduce([:], combine: reducer)
But it crashes when I want to run it. Not sure why, I get a EXC_BAD_INSTRUCTION.
I'd appreciate any help.
let numbers = [1,2,3,4,1,2,2,1]
var results = [Int: Int]()
Set(numbers).forEach { number in results[number] = numbers.filter { $0 == number }.count }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
Actually I'm not very sure if this is what you want. I just looked at your examples.
Using NSCountedSet:
var objects = [1,2,3,4,1,2,2,1]
let uniques = NSCountedSet(array: objects)
uniques.forEach { results[$0 as! Int] = uniques.countForObject($0) }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
I would expect the crash to be ocurring on this line:
acc[num]! += 1
The first time this is called for a number, the entry doesn't exist in the dictionary yet so acc[num] is nil. Forcefully unwrapping it would cause a crash.
Not sure if this is the best solution but you can simple check for this case:
if (acc[num]) {
acc[num]! += 1
} else {
acc[num] = 1
}
Cleaner code from #vacawama in the comments:
acc[num] = (acc[num] ?? 0) + 1
Here's an extension to Array that does what you're asking:
extension Array where Element: Hashable {
var grouped: [Element:Int] {
var dict = [Element:Int]()
self.forEach { dict[$0] = (dict[$0] ?? 0) + 1 }
return dict
}
}
The key is the closure: { dict[$0] = (dict[$0] ?? 0) + 1 }.
It takes the current value in the array, tests to see if it's a key in the dictionary, returns the value for that key if it exists or 0 if it doesn't, then adds one and sets the key:value to be the pair of the current value and occurrences so far.
Example use:
[1,2,3,4,1,2,2,1].grouped // => [2: 3, 3: 1, 1: 3, 4: 1]
You need something like this:
if let _ = acc.indexForKey(num) {
acc[num]! += 1
}
else {
acc[num] = 1
}
It's sort of unclear what you're asking for, but here's a function that will take an array of ints and return a dictionary with the number as the key, and the count as the value:
func getDictionaryOfCounts(accumulator: [Int]) -> [Int : Int] {
var countingDictionary: [Int : Int] = [:]
accumulator.forEach { (value) in
if countingDictionary[value] != nil {
countingDictionary[value]! += 1
}
else{
countingDictionary[value] = 1
}
}
return countingDictionary
}

Change specific values in an array

I have 3 arrays of type Int that have 5 values in each in them. I'm trying to create a function that replaces the values based on conditions. For example, array1, if a value of an index is between 2-5, replace with a randomly generated number between 1-6.
Here is what I have so far
import Foundation
func newRandomNumbers(#array1: [Int], array2: [Int], array3: [Int]) {
for i in 0..<5 {
switch (array1[i]) {
case 2, 3, 4, 5:
let randomNumber = Int(1 + arc4random() % 6)
array1[i] = randomNumber
break;
default:
break;
}
switch (array2[i]) {
case 2, 3, 4, 5:
array2[i]
break;
default:
break;
}
switch (array3[i]) {
case 1, 2, 3, 4, 5:
array3[i]
break;
default:
break;
}
}
}
I get an error " Cannot assign to immutable value of type 'Int'"
When I rewrite a simplified function that does the same purpose I don't get the error, but strangely not all the numbers are replaced.
import Foundation
var newArray = [2,3,4,5,6]
func newRandom(#array1: [Int]){
for i in 0..<5 {
switch(array1[i]) {
case 2, 3, 4, 5:
let randomNumber = Int(1 + arc4random() % 6)
newArray[1] = randomNumber
default:
break;
}
}
}
newRandom(array1: newArray)
newArray
Not sure how to resolve. Recommendations on how to clean up my code would also be appreciated. Thanks!
"Cannot assign to immutable value of type 'Int'" is because the parameters are actually constants. You can read the line:
func newRandomNumbers(#array1: [Int], array2: [Int], array3: [Int])
as:
func newRandomNumbers(#array1 let array1: [Int], let array2: [Int], let array3: [Int])
You can change it by putting var in front of the parameter and return the array. Another option is using & to make it a reference, so you don't have to return a new array.
I would suggest you look into the array's map function:
https://developer.apple.com/library/ios/documentation/General/Reference/SwiftStandardLibraryReference/Array.html
you would use for example:
array.map {
if ($0 satisfies condition) {
generate random number
}
}
You can use inout for each of your function parameters and add & before your variable name when calling the function, it will modify the array in place:
func newRandom(inout myArray: [Int]) {
for (index, element) in enumerate(myArray) {
switch element {
case 2...5:
myArray[index] = Int(1 + arc4random() % 6)
default:
break
}
}
}
var newArray = [2,3,4,5,6]
newRandom(&newArray)
println(newArray) // "newArray" was modified, contains random numbers
Note:
switch element {
case 2...5:
is equivalent to:
if element >= 2 && element <= 5 {
SWIFT 2.0 UPDATE:
As of Swift 2.0, enumerate is now to be called on the sequence itself. Also, println has been deprecated in favor of print:
func newRandom(inout myArray: [Int]) {
for (index, element) in myArray.enumerate() {
switch element {
case 2...5:
myArray[index] = Int(1 + arc4random() % 6)
default:
break
}
}
}
var newArray = [2,3,4,5,6]
newRandom(&newArray)
print(newArray)