So here I have a basic setup
var preferenceSpecification = [String : String?]()
preferenceSpecification["Key"] = "Some Key"
preferenceSpecification["Some Key"] = nil
preferenceSpecification["DefaultValue"] = "Some DefaultValue"
print(preferenceSpecification)
var defaultsToRegister = [String : String]()
if let key = preferenceSpecification["Key"], let defaultValueKey = preferenceSpecification["DefaultValue"] {
defaultsToRegister[key] = preferenceSpecification[defaultValueKey]!
}
But the error points out where it demands that I force unwrap this, to be like this:
defaultsToRegister[key!] = preferenceSpecification[defaultValueKey!]!
Which doesn't make sense, because keyValue and defaultValue already are unwrapped
When you extract a value from a dictionary like this using subscript
[String: String?]
you need to manage 2 levels of optional. The first one because the subscript returns an optional. The second one because the value of you dictionary is an optional String.
So when you write
if let value = preferenceSpecification["someKey"] {
}
you get value defined as an optional String.
Here's the code to fix that
if let
optionalKey = preferenceSpecification["Key"],
key = optionalKey,
optionalDefaultValueKey = preferenceSpecification["DefaultValue"],
defaultValueKey = optionalDefaultValueKey,
value = preferenceSpecification[defaultValueKey] {
defaultsToRegister[key] = value
}
Suggestions
You should avoid force unwrapping as much as possible. Instead you managed to put 3 ! on a single line!
You should also try to use better name for your constants and variables.
You could also define an extension which helps get rid of the double optional situation.
extension Dictionary where Value == Optional<String> {
func flattened(_ key: Key) -> Value {
if let value = self[key] {
return value
}
return nil
}
}
Usage: preferenceSpecification.flattened("someKey")
Related
is there a way to force dictionary to return a non optional value? , and if its doesn't exists just assert
var dic = Dictionary<String,String>()
dic["0"] = "Zero"
let z = dic["0"] // is there a way that the 'z' variable will not be an optional (without doing an optional unwrapping, and so) , and if the value don't exists the program will assert
is that kind of thing is possible with an extension of dictionary ?
You can use the Nil-Coalescing Operator (a ?? b)
var dic = Dictionary<String,String>()
dic["0"] = "Zero"
let z = dic["0"] ?? "Any Default String"
z constant will be of type String.
A more common way to deal with optionals is using if let and guard let
You can create your own custom Dictionary subscript function in an extension. You'll need to add a label to distinguish it from the normal Dictionary subscripting.
Example:
extension Dictionary {
subscript(force key: Key) -> Value {
if let value = self[key] {
return value
}
fatalError("Forced key \(key) wasn't found in Dictionary")
}
}
var dic = [String : String]()
dic["0"] = "Zero"
let z = dic[force: "0"]
print(z)
print(type(of: z))
Output:
Zero
String
To set a dictionary value, I am doing like below.
var dic: [String:Any]?
dic = [String:Any]()
dic!["name"] = "my name"
dic!["email"] = "aaa#bbb.com"
dic!["nickname"] = "Grrrrrrrr"
I think the exclamation mark (!) is superfluous.
That is, I'd like to set without '!'.
dic = [String:Any]()
dic["name"] = "my name"
dic["email"] = "aaa#bbb.com"
dic["nickname"] = "Grrrrrrrr"
Is there any way to use in short to set a value to optional variable?
Having read the comments, the solution below could be what you are looking for.
var dic = [String: Any]() //Empty dictionary
func processResultOfAlamofire(_ result: [String: Any]?) {
dic = result ?? [String: Any]()
}
If the result of Alamo is nil, dicwill be empty instead of nil. The ?? operator could be written as:
if result == nil {
dic = [String: Any]()
} else {
dic = result!
}
I think that you should update your code, Don't use direct dictionary in your viewcontroller. set optional variable in your model. and when ever you use those values use it with conditional checking like, if let value = somethingOptional{}
or use guard in your API call function so it will never update or effect any view if you are getting values null from API
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.
I want to initialize a dictionary with a dictionary nested inside like this:
var a = [Int:[Int:Float]]()
a[1][2] = 12
But I get an error:
(Int:[Int:Float]) does not have a member named 'subscript'
I've hacked at a variety of other approaches, all of them running into some kind of issue.
Any idea why this doesn't work?
You can create your own 2D dictionary like this:
struct Dict2D<X:Hashable,Y:Hashable,V> {
var values = [X:[Y:V]]()
subscript (x:X, y:Y)->V? {
get { return values[x]?[y] }
set {
if values[x] == nil {
values[x] = [Y:V]()
}
values[x]![y] = newValue
}
}
}
var a = Dict2D<Int,Int,Float>()
a[1,2] = 12
println(a[1,2]) // Optional(12.0)
println(a[0,2]) // nil
The point is you access the element via a[x,y] instead of a[x][y] or a[x]?[y].
It's giving you that error because your first subscript returns an optional so it may return a dictionary or nil. In the case that it returns nil the second subscript would be invalid. You can force it to unwrap the optional value by using an exlamation point.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]![2]
If you aren't positive that a[1] is non-nil you may want to safely unwrap with a question mark instead.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]?[2]
You can also assign using this method. (As of Beta 5)
var a = [Int:[Int:Float]]()
a[1] = [Int: Float]()
a[1]?[2] = 12.0
a[1]?[2] //12.0
Another way to do it is with an extension to the standard dictionary:
extension Dictionary {
mutating func updateValueForKey(key: Key, updater: ((previousValue: Value?) -> Value)) {
let previousValue = self[key]
self[key] = updater(previousValue: previousValue)
}
}
Example:
var a = [Int:[Int:Float]]()
a.updateValueForKey(1) { nestedDict in
var nestedDict = nestedDict ?? [Int:Float]()
nestedDict[2] = 12
return nestedDict
}
I've been playing with swift and am getting quite tortured! Consider:
var myDict : Dictionary <String, String>
//DO SOME MAGIC TO POPULATE myDict with values
<magic being done>
//Now myDict has values. Let's parse out the values of myDict
//This doesn't work
let title : String = myDict["title"]
//This does
let title : String? myDict["title"]
This is because it isn't known whether the key is in the dictionary. What I want to say, though, is "If the title key is in the dictionary, give me that value, else, just give me an empty string"
I could probably write:
var myTitle : String
if let title : String = myDict["title"] {
myTitle = title
} else {
myTitle = ""
}
I believe that works...BUT...it's quite a lot of code for EACH key of the dictionary. Does anyone have any ideas in the swift world on how this is supposed to be written?
RD
You could write an extension on optional:
extension Optional {
/// Unwrap the value returning 'defaultValue' if the value is currently nil
func or(defaultValue: T) -> T {
switch(self) {
case .None:
return defaultValue
case .Some(let value):
return value
}
}
}
Then you can do:
myDict["title"].or("")
This would also work for all optionals.
Note: I started a module to add common helpers like this or on Optional to swift.
You unwrap the value either explicitly:
let title : String = myDict["title"]!
or implicitly:
let title : String! = myDict["title"]
Note that you still have to check whether title is nil or not unless you are really sure it's there.
Edit:
Here's a sample global operator overload for any optional for type T:
#infix func | <T: Any>(lhs: T?, rhs: T!) -> T! {
if lhs {
return lhs!
}
return rhs
}
var myDict : Dictionary <String, String> = ["a": "b"]
let title1 = (myDict["a"] | "") // "b"
let title2 = (myDict["title"] | "") // ""