Cannot assign through subscript: 'dict' is a 'let' constant - swift

I have a very simple problem with Swift.
I created this function:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
func correctAges(dict:[String:Int]) {
for (name, age) in dict {
guard age >= 0 else {
dict[name] = 0
continue
}
}
}
correctAges(dict:dictAges)
But I don't understand the error:
"cannot assign through subscript: 'dict' is a 'let' constant, dict[name] = 0"
How can I solve it?

Input arguments to functions are immutable inside the function body and a Dictionary is a value type, so the dict you see inside the function is actually a copy of the original dictAges. When you call a function with a value type as its input argument, that input argument is passed by value, not passed by reference, hence inside the function you cannot access the original variable.
Either declare the dict input argument as inout or if you'd prefer the more functional approach, return a mutated version of the dictionary from the function.
Functional approach:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
func correctAges(dict:[String:Int])->[String:Int] {
var mutatedDict = dict
for (name, age) in mutatedDict {
guard age >= 0 else {
mutatedDict[name] = 0
continue
}
}
return mutatedDict
}
let newDict = correctAges(dict:dictAges) //["Michael": 20, "Bob": 0, "John": 40]
Inout version:
func correctAges(dict: inout [String:Int]){
for (name,age) in dict {
guard age >= 0 else {
dict[name] = 0
continue
}
}
}
correctAges(dict: &dictAges) //["Michael": 20, "Bob": 0, "John": 40]

Your expectation that you can mutate a dictionary passed into a function is wrong, for the simple reason that a dictionary in Swift is a value type. And the types inside the dictionary, String and Int, are values too. This means that, in effect, the parameter dict is a copy of the original dictAges. Nothing that you do to dict can have any effect on dictAges.
This is a sign that you should rethink your entire architecture. If it was wrong to enter a negative number as an age, you should have caught that up front, as it was being entered. In effect, your entire use of a dictionary of Ints as the model here is probably incorrect. You should have used a dictionary of Person, where Person is a struct with an age and a setter that prevents an age from being negative in the first place.
If you must have a function that runs through an arbitrary dictionary and fixes the values to be nonnegative, make that a feature of dictionaries themselves:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
extension Dictionary where Value : SignedInteger {
mutating func fixAges() {
for (k,v) in self {
if v < 0 {
self[k] = 0
}
}
}
}
dictAges.fixAges()

In general, it's not good to mutate a sequence as you're looping through it, which is why you only get an immutable copy of the dictionary in your code.
Swift 4
I think a nicer approach could be to use map, and to take a more functional approach:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
func correctAges(dict:[String:Int]) -> [String:Int]
{
let corrected = dict.map { (name, age) in age > 0 ? (name, age) : (name, 0) }
return Dictionary(uniqueKeysWithValues: corrected)
}
dictAges = correctAges(dict: dictAges)
print(dictAges) // ["Michael": 20, "Bob": 0, "John": 40]
This way you can reuse this method for any [String:Int] dictionary.

Related

Cannot assign value of type 'Int' to type 'Int?'

I am trying to find out the count of first and last name value exit in an array of a and return a result as[String: Int] a count with the same key.
I am getting error on this line newResult[arg.key] = counts . Cannot assign value of type 'Int' to type 'Int?
func abbreviation(a:[String], b: [String : String]) ->[String : Int] {
let dict = b.reduce([String : Int]()){ (result, arg) in
var newResult = result
let counts = a.reduce(0) { (newcount, value) -> Int in
let count = newcount + (value.components(separatedBy:arg.value).count - 1)
return count
}
return newResult[arg.key] = counts
}
return dict
}
//result
let dict = abbreviation(a:["This is chandan kumar and chandan kumar and new chandan","check chandan kumar","non ame"], b:["first":"chandan","last":"kumar"])
The error message is so confusing, and you may need to be accustomed to take it as Swift cannot infer some types in this context.
With this line:
return newResult[arg.key] = counts
Do you know what is returned with this return-statement? It's a Void, also known as an empty tuple. (Void is the result type of assignment statement in Swift.) You might have expected newResult would be the result of the closure, but that sort of things would not happen unless you explicitly write return newResult.
Try changing the line in to the following:
newResult[arg.key] = counts
return newResult
You are trying to return the result of an assignment expression:
return newResult[arg.key] = counts
or maybe you are trying to assign to the result of a return statement? This line doesn't make sense which way you look at it. You should separate the two things you are doing:
newResult[arg.key] = counts
return newResult
It seems like in this situation, you should use the other overload of the reduce method - reduce(into:_:).
The reduce method you are currently using requires you to return a new value every time, but you are just adding a KVP to a dictionary i.e. modifying an existing value. This means that you are creating lots of copies of dictionaries. This is a good sign that reduce(into:_:) might be a better fit.
func abbreviation(a:[String], b: [String : String]) ->[String : Int] {
// notice the parameter label "into:"
let dict = b.reduce(into: [String : Int]()){ (result, arg) in
let counts = a.reduce(0) { (newcount, value) -> Int in
let count = newcount + (value.components(separatedBy:arg.value).count - 1)
return count
}
result[arg.key] = counts // with reduce(into:_:), you don't return anything, just modify the first argument
}
return dict
}

What is the best way in Swift 4+ to store a set of homogenous arrays for various types in a dictionary?

Consider a situation where we want to have a dictionary of arrays, with each array being a homogeneous collection of values of some type (which may be a struct or a primitive type). I'm currently using the ObjectIdentifier of the type defining it thusly:
let pInts : [UInt32] = [4, 6, 99, 1001, 2032]
let pFloats : [Float] = [3.14159, 8.9]
let pBools : [Bool] = [true, false, true]
let myDataStructure : [ObjectIdentifier : [Any]] = [
ObjectIdentifier(Float.self) : pFloats,
ObjectIdentifier(UInt32.self) : pInts,
ObjectIdentifier(Bool.self) : pBools
]
The issue here is that when traversing the data structure, Swift doesn't know that the objects in each list are homogeneous. Since swift is statically typed, I'm guessing it is not possible to typecast the [Any] lists using the ObjectIdentifier keys. Consider this traversal pseudocode:
for (typeObjId, listOfValuesOfSometype) in myDataStructure {
// do something like swap values around in the array,
// knowing they are homogeneously but anonymously typed
}
So, is there some metatype machinery I can concoct to represent this data structure in a way that does not anticipate the list of actual types that will have arrays in it?
I'm not exactly sure what you want to accomplish, Inside the dictionary loop the arrays will always be of type Any, but if you want to move items in the arrays around, you could just do that. Just reassign the array first to a var and then put it back in the dictionary.
If you do want to loop through the items of a specific type, then you could use the array helper function below.
func testX() {
let pInts: [UInt32] = [4, 6, 99, 1001, 2032]
let pFloats: [Float] = [3.14159, 8.9]
let pBools: [Bool] = [true, false, true]
var myDataStructure: [ObjectIdentifier: [Any]] = [
ObjectIdentifier(Float.self): pFloats,
ObjectIdentifier(UInt32.self): pInts,
ObjectIdentifier(Bool.self): pBools
]
// Swap the first 2 items of every array
for d in myDataStructure {
var i = d.value
if i.count > 1 {
let s = i[0]
i[0] = i[1]
i[1] = s
}
myDataStructure[d.key] = i
}
// Now dump all data per specific type using the array helper function.
for i: UInt32 in array(myDataStructure) {
print(i)
}
for i: Float in array(myDataStructure) {
print(i)
}
for i: Bool in array(myDataStructure) {
print(i)
}
}
func array<T>(_ data: [ObjectIdentifier: [Any]]) -> [T] {
return data[ObjectIdentifier(T.self)] as? [T] ?? []
}

Updating variable value from dictionary and best practice

As I progress into my Swift education, the time as come for me to ask for help about best practice and code optimization.
My app has become more and more complex every day and the current situation is as follows: I started using dictionaries and arguments to use a single function that can process a lot of different variables depending on the situation, which seems to be better practice than using 5 different functions that will do the same thing only with different variables.
I now have two files as follows:
Main1.swift:
class Main1 {
static var value1 : Int = 1
func updateValue(_ value: String) {
let dict : [String : Int] = ["value1": Main1.value1]
let dict1 = dict[value]
guard var value = dict1 else { return }
value = value + 1 // <- trying to update `static var value1`'s value from 1 to 2 here
print(value)
}
}
Main2.swift:
class Main2 {
func updateValue(_ value: String) {
let dict : [String : Int] = ["value1": Main1.value1] // <- thinking `value1` would now be 2
let dict1 = dict[value]
guard var value = dict1 else { return }
value = value + 1 // <- trying to get 3 here
print(value)
}
}
These classes are simplified versions of my code but the logic is the same: I am trying to use variables loaded from dictionaries and update their values to be used in another file and function:
Main1().updateValue("value1") //2
Main2().updateValue("value1") //2 <--- I need 3 here!
-> What exactly am I trying to achieve here?
To update the reference (static var value1 : Int = 1) value while accessing it through the convenience of a dictionary (or different method but you get the point about convenience).
In fact I am trying to do Main1.value1 = Main1.value1 + 1 while accessing Main1.value1 through a dictionary, which is impossible because I am not accessing the reference here.
I know this can't work, I have 3 different copies of value here but I don't how to update the variable value without using another global variable... I need your help to find a better logic.
I am open to any suggestion or thinking. I am not asking for code solution (which would be great anyway) but I'd love to have my education re-centered a little bit, I am starting to lose myself learning all by myself and the frustration comes from that I don't know what to be looking for anymore.
EDIT BASED ON COMMENTS
As per the comments below, here's a potential solution:
class Main1 {
static var dict: [String: Int] = ["value1": 1]
func updateValue(_ key: String) {
guard var value = dict[key] else { return }
value = value + 1
print(value)
dict[key] = value
}
}
ORIGINAL ANSWER
In Swift, [String : Int], String and Int are value types, as opposed to their Objective-C counterparts NSDictionary, NSString and NSNumber, which are reference types.
This means that when you do guard var value = dict1 else { return }, value is now a copy of what the dictionary contained, not a reference to that piece of data inside the dictionary.
So when you do value = value + 1 you're setting the new variables value, but not the contents of the dictionary.
Following your logic, you need to put value back into the dictionary, like this:
func updateValue(_ value: String) {
var dict : [String : Int] = ["value1": Main1.value1] // <- Change this to a var
let dict1 = dict[value]
guard var intValue = dict1 else { return }
intValue = intValue + 1 // <- trying to update `static var value1`'s value from 1 to 2 here
print(intValue)
dict[value] = intValue // <- ADD THIS
}

Native Swift Dictionary by Reference

What I'm trying to do:
a class that has several (say 10) instance variables of dictionary type (mutable var).
a method that (depending on arguments, etc.) picks a dictionary an updates it.
In ObjC, this is fairly easily accomplished using NSMutableDictionary. In Swift, this is more tricky, since the dictionary gets copied on into the local variable.
I think the best way to explain what I'm trying to achieve is via a code sample:
class MyClass {
/// There are several dictionaries as instance variables
var dict1: [String : String] = [ : ]
var dict2: [String : String] = [ : ]
// ...
/// This method should change a value in one of the dictionaries,
/// depending on the argument.
func changeDictAtIndex(index: Int) {
var dict: [String : String]
if index == 0 {
dict = dict1
}else{
dict = dict2
}
dict["OK"] = "KO"
// This won't work since Swift copies the dictionary into
// the local variable, which gets destroyed at the end of
// the scope...
}
}
let obj = MyClass()
obj.changeDictAtIndex(0)
obj.dict1 // Still empty.
Question: Is there a native way to do this (native meaning without using NSMutableDictionary)?
P.S.: I'm aware of the inout attribute, but that works AFAIK only with function arguments, which doesn't really solve anything...
EDIT:
I'm currently solving this via closure:
var dictSetter: (key: String, value: String) -> Void
if index == 0 {
dictSetter = { self.dict1[$0] = $1 }
}else{
dictSetter = { self.dict2[$0] = $1 }
}
dictSetter(key: "OK", value: "KO")
As you may already aware, you can use inout to solve the problem
func updateDict(inout dict: [String : String]) {
dict["OK"] = "KO"
}
func changeDictAtIndex(index: Int) {
if index == 0 {
updateDict(&dict1)
}else{
updateDict(&dict2)
}
}
Question: Is there a native way to do this (native meaning without using NSMutableDictionary)?
I have rewritten your class, note the changes:
Different syntax for empty dictionary
ChangeDictAtIndex function now takes in a dictionary you want to replace.
The instance variables are being set to the passed in dict.
I would look at the Apple's The Swift Programming Language (Swift 2.1) section on the basics and collection types.
class MyClass {
// There are several dictionaries as instance variables
var dict1 = [String : String]()
var dict2 = [String : String]()
func changeDictAtIndex(index: Int, dict: [String : String]) {
if index == 0 {
dict1 = dict
} else {
dict2 = dict
}
}
}
Usage:
let obj = MyClass()
obj.changeDictAtIndex(0, dict: ["MyKey" : "MyValue"])
print(obj.dict1)

Swift Dictionary default value

A pattern I've gotten used to with Python's defaultdicts is a dictionary that returns a default value if the value for a given key has not been explicitly set. Trying to do this in Swift is a little verbose.
var dict = Dictionary<String, Array<Int>>()
let key = "foo"
var value: Array<Int>! = dict[key]
if value == nil {
value = Array<Int>()
dict[key] = value
}
I realize I can make a class that does this, but then the actual Dictionary has to be accessed through a property to use any of the other normal Dictionary methods
class DefaultDictionary<A: Hashable, B> {
let defaultFunc: () -> B
var dict = Dictionary<A, B>()
init(defaultFunc: () -> B) {
self.defaultFunc = defaultFunc
}
subscript(key: A) -> B {
get {
var value: B! = dict[key]
if value == nil {
value = defaultFunc()
dict[key] = value
}
return value
}
set {
dict[key] = newValue
}
}
}
Is there a better pattern for this?
This changed in Swift 4, and there's now a way to read a key's value or provide a default value if the key isn't present. For example:
let person = ["name": "Taylor", "city": "Nashville"]
let name = person["name", default: "Anonymous"]
This is particularly useful when modifying dictionary values, because you can write code like this:
var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()
for show in favoriteTVShows {
favoriteCounts[show, default: 0] += 1
}
I covered this change and others in my article What's new in Swift 4.
Using Swift 2 you can achieve something similar to python's version with an extension of Dictionary:
// Values which can provide a default instance
protocol Initializable {
init()
}
extension Dictionary where Value: Initializable {
// using key as external name to make it unambiguous from the standard subscript
subscript(key key: Key) -> Value {
mutating get { return self[key, or: Value()] }
set { self[key] = newValue }
}
}
// this can also be used in Swift 1.x
extension Dictionary {
subscript(key: Key, or def: Value) -> Value {
mutating get {
return self[key] ?? {
// assign default value if self[key] is nil
self[key] = def
return def
}()
}
set { self[key] = newValue }
}
}
The closure after the ?? is used for classes since they don't propagate their value mutation (only "pointer mutation"; reference types).
The dictionaries have to be mutable (var) in order to use those subscripts:
// Make Int Initializable. Int() == 0
extension Int: Initializable {}
var dict = [Int: Int]()
dict[1, or: 0]++
dict[key: 2]++
// if Value is not Initializable
var dict = [Int: Double]()
dict[1, or: 0.0]
Unless I'm misunderstanding defaultdict in Python, I don't see how nil coalescing wouldn't work for you. Let's say you had a dictionary of type [Int:Int], and you wanted it to return 0 by default. With nil coalescing it looks like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4] ?? 0
You mentioned in a comment that that wouldn't work because it wouldn't update the dictionary. I don't understand the problem, though: why would you need to update the dictionary if you knew that every instance of nil would be replaced by your default? Maybe I'm missing something here but it seems like defaults and nil coalescing are (in practice) the same.
You can change the syntax a little, if it makes things more clear:
extension Dictionary {
subscript(key: Key, or r: Value) -> Value {
get { return self[key] ?? r }
set { self[key] = newValue }
}
}
In this case, the example above could be written like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4, or: 0]
In this case, mutating methods can work on the keys, like this:
var dict = [2: 8, 3: 64, 1: 10]
dict[2, or: 0]++
dict // [2: 9, 3: 64, 1: 10]
dict[4, or: 0]++
dict // [2: 9, 3: 64, 1: 10, 4: 1]
This extension is similar to the default subscript in Swift 4, with the difference that it will actually store the default value in the dictionary.
(It's also similar to QByte's answer, with the difference that it uses an autoclosure to prevent accessing the default when not needed).
extension Dictionary {
subscript(key: Key, setDefault defaultValue: #autoclosure () -> Value) -> Value {
mutating get {
return self[key] ?? {
let value = defaultValue()
self[key] = value
return value
}()
}
}
}
Note that no setter is defined for the subscript as the standard default subscript already fulfills this purpose.
Example:
var items = [String: ComplexItem]()
let item1 = items["milk", setDefault: ComplexItem()]
let item2 = items["milk", setDefault: ComplexItem()]
Here the ComplexItem is only created once because the dictionary retained it after the first access.