Change specific values in an array - swift

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)

Related

cannot pass immutable inout value as parameter: function call returns immutable value

I get the above message in a project, I was trying to test the code and needed the ability to debug.
//
// main.swift
// Chapter7Example2
//
// Created by Mark Edward King on 20/04/2021.
//
import Foundation
func removingOnce(_ item: Int, from array: inout [Int]) -> [Int] {
for i in 0..<array.count {
if array[i] == item {
// Swap the first two elements
array.remove(at: i)
return array
}
}
return array
}
var arrayVal : [Int] = [1, 2, 3, 4, 5]
arrayVal = removingOnce( 2, from: &arrayVal)
for i in arrayVal {
print(i)
}
func removing(_ item: Int, from array: inout [Int]) -> [Int] {
var notFound = true
var arrayRes : [Int] = []
for i in array {
if array[i] != item {
notFound = false
}
}
if notFound == false {
return array
} else if array == [] {
return array
} else {
for i in array {
if array[i] == item {
// Remove the first element
array.remove(at: i)
return arrayRes = removing(item, from: &Array(array[1..<array.count]))
}
}
return array
}
}
func reverse( from array: inout [Int]) -> [Int] {
var arrayRes : [Int] = []
if array == [] {
return array
}
else if array.count == 1 {
return array
}
else {
// Swap first two members
array.swapAt(0, 1)
// Break down the array into smaller parts
arrayRes = reverse( &Array(array[2..<array.count]))
// Append one of the arrays which has been reversed and append the solved part
return array[0...1] += arrayRes
}
}
var arrayVal2 : [Int] = [1, 2, 2, 2, 5]
arrayVal2 = removing( 2, from: &arrayVal2)
for i in arrayVal2 {
print(i)
}
/*func middle(_ array: [Int]) -> Int? {
if array.count % 2 == 0 {
return array[array.count / 2 - 1]
}
else {
return array[array.count / 2]
}
}*/
var arrayVal3 : [Int] = [1, 2, 3, 4, 5]
I just need help with the removing function. There are actually two errors but. The other error is cannot convert return type of () to [Int], not sure why this is related so help on this problem would also be appreciated.
There are errors at almost every lines of your removing function, I'll try to address each one assuming that your function is supposed to remove every occurence of item.
func removing(_ item: Int, from array: inout [Int]) -> [Int] {
As said by #Shadowrun, you must choose between returning the modified array as the return type or updating it via the inout parameter, not both.
var array = array
Here you make a local copy of array with a shadowing name (i.e. you use the same name), which defeats the purpose of using an inout parameter as you never update it.
var arrayRes : [Int]
Using an initialized variable is very bad practice in Swift, at least you should initialize and empty array.
for i in array {
if array[i] != item {
notFound = false
}
}
You tried to iterate over the array elements instead of the array indices. The fast that the compiler did not complained is by pure coincidence as array elements are also of Int type, the same as Swift array indices.
Another issue is that you don't really need indices but only elements, here is the correct way:
for element in array {
if element != item {
notFound = false
}
}
if notFound == false {
...
} else if array == [] {
...
} else {
for element in array {
if element == item { ... }
}
}
What is inside the if condition of the else branch will never be executed because you previously checked that notFound == false in the first if condition. This means that notFound is necessarily true in the else branch, meaning all elements are different that item, so element == item always returns false.
array.remove(at: i)
Doing an operation that mutates the array you are currently iterating is almost always guaranteed to be an error and will certainly cause you troubles and runtime errors at some point (do it only if you know that it is safe).
return arrayRes = removing(item, from: &array[1...array.count])
This is wrong in many ways:
you are returning the value of an assignment, i.e a code line with a = sign. This does not make sense as you probably want to return the value of removing(...) rather than the value of the assignment. And an assignment does not have any value (materialized by the Void type).
This call (minus the previous error) is recursive, I don't now if this is intended but I doubt you understand how this works.
The removing(...) func accepts an array to [Int] as argument, here you are trying to pass an array Slice array[1...array.count]. Also remove the & sign if not using the inout parameter.
I hope that you know that Swift array indices start at 0, not 1 and that the 1 is intended in your code.
All that said, the origin of your error is here:
return arrayRes = removing(item, from: &Array(array[1...array.count]))
Array(array[1...array.count]) is an immutable object because is is instantiated inline, at the call site. However, it should be mutable because removing accepts an inout parameter, which requires the parameter to be mutable.
The second error is at the same line, you are trying to return the value of an assignment (statement with an = sign) instead of the return value of the function. An assignment has a return type of Void (meaning that it does not return something useful) which is incompatible with your function return type: [Int].
I don't know if this is homework or some kind of programming training, but if this is the case you should start with the basis, the Official Swift Book by Apple is a good starting point. If not the case you can achieve the same thing with:
var array = [1, 2, 3]
func removing(_ item: Int, from array: inout [Int]) {
array.removeAll { $0 == item }
}
removing(2, from: &array)
print(array)
func reversed(_ array: inout ([Int]) -> [Int]) {
Is a function that takes a single argument: an inout function of type [Int] -> [Int] - not what you want
You should either make it a function that mutates a [Int] via an inout [Int] parameter: like func reversed(_ array: inout [Int]) or else make it a function from [Int] -> [Int] like func reversed(_ array: [Int]) -> [Int]
Functions of the form A -> A are equivalent to functions of the form inout A -> Void, and you should pick one style or the other.

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]
}

Casting arrays to specific types in Swift

Say I have two arrays:
var exterior: Array<(name: String, value: (code: Code, pass: Bool))> = []
var interior: Array<(name: String, value: (code: Code, type: Type, pass: Bool))> = []
I have a UISegmentedControl that, depending on which segment is selected, will show data from the respective array. To reduce boilerplate, I'd like to use one fuction for setup:
func build(section: Section) {
var data: Array<Any>
switch section {
case .Exterior:
data = exterior
case .Interior:
data = interior
}
for i in 0...data.count - 1 where i % 4 == 0 {
for y in i...i + 4 {
guard y < data.count - 1 else {
break
}
switch section {
case .Exterior:
let v = data as! Array<(String, (Report.Code, Bool))>
// Do stuff here...
case .Interior:
let v = data as! Array<(String, (Report.Code, Report.Type, Bool))>
// Do stuff here...
}
}
}
}
This won't work since I cannot cast to an array that holds Any. If I change the type of both interior and exterior to Any and try casing them to their respective types, I get an error: can't unsafeBitCast between types of different sizes. What are my options in this situation?
You cannot cast Array<Any> to Array<AnyOther>, because there is no inheritance between Array<Any> and Array<AnyOther>. You should actually convert such arrays like so:
let xs: [Any] = [1, 2, 3, 4, 5]
let ys: [Int] = xs.flatMap { $0 as? Int }
print(ys.dynamicType) // Array<Int>
print(ys) // [1, 2, 3, 4, 5]

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
}

'T' is not identical to 'Int'

I'm trying some sample code in Swift and combined two principles: Array.map and Extension. Unfortunately, I get the error code "'T' is not identical to 'Int'" (line 3). So my question should probably be: how can I transform a T array into a Int array?
Thanks in advance for your answers!
Cheers.
extension Array {
func translateToDigitalNames()-> [String] {
var numbers : [Int] = self
let digitNames =
[0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"]
var strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
return strings
}
}
let x = [26, 158, 610]
x.translateToDigitalNames()
I would split the problem into 3 distinct subproblems.
The first is translating an integer into a string representation, implemented as a static method:
private static func translateToDigitalName(var number: Int) -> String {
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
The reasons for making it a separate function are: separation of concerns, reusability, readability.
Next, we can implement a static method that, given an array of integers, returns an array of their respective string representations:
private static func translateToDigitalNames(numbers: [Int])-> [String] {
return numbers.map { self.translateToDigitalName($0) }
}
Last, the actual array extension. The approach I'm using is to filter the array by excluding elements not castable to Int:
func translateToDigitalNames()-> [String] {
let numbers: [Int] = self.filter { $0 is Int }.map { $0 as Int }
return Array.translateToDigitalNames(numbers)
}
If you want the translation to fail if at least one array element is not an Int, just add an explicit check and return nil in that case:
func translateToDigitalNames()-> [String]? {
let numbers: [Int] = self.filter { $0 is Int }.map { $0 as Int }
if numbers.count != self.count {
return nil
}
return Array.translateToDigitalNames(numbers)
}
Note that the return type is now an optional.
Last thing, rather than creating the digitNames array at every function invocation, I've moved it as a private global and immutable variable
The full code is available on this gist
Side note: the translateToDigitalNames and translateToDigitalName can be moved outside of the array extension, as global and public functions - and that's actually what I'd recommend in case you need to reuse them in the future.
You can create the numbers array with a map function as well, like:
var numbers = map { $0 as Int }
It's not a good idea to write such kind of extension since it applies only to [Int] arrays. What should happen if you would do
["some", "strings"].translateToDigitalNames()
Instead use a function like this:
func translateToDigitalNames(numbers:[Int])-> [String] {
let digitNames =
[0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"]
var strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
return strings
}
let x = [26, 158, 610]
translateToDigitalNames(x)
So the compiler ensures you always supply a [Int]