How to add a extra values for the keys on dictionaries? - swift

For the following variable
var dict2 = ["key1" : "value1", "key2" : [ "value1" , "value2" ]]
How to add a third value for the second key of the following dictionary?

If I can reformulate your dict2 declaration slightly:
var dict2 = ["key1" : ["value1"], "key2" : [ "value1" , "value2" ]]
then you can append an extra item to key2 like this:
dict2["key2"]?.append("value3")
However, you will probably need to be careful to check that key2 was already present. Otherwise, the above statement will do nothing. In which case you can write:
if dict2["key2"]?.append("value3") == nil {
dict2["key2"] = ["value3"]
}
Why did I change the original declaration? With the version I gave, dict2 will be of type [String:[String]]. But with your version, what Swift is doing is declaring a much more loosely typed [String:NSObject]. This compiles, but behaves very differently (contents will have reference not value semantics, you will have to do type checks and casts frequently etc), and is probably best avoided.

#AirspeedVelocity provides a working solution, but requiring a small change to the way the data is defined - but that is what I would do myself, if possible.
However if you have to stick with the original data format, you can use this code:
var dict2: [String : AnyObject] = ["key1" : "value1", "key2" : [ "value1" , "value2" ]]
var array = dict2["key2"] as? [String]
array?.append("value3")
dict2["key2"] = array
First we make explicit the dict2 type, a dictionary using strings as keys and AnyObject as values.
Next we extract the value for the key2 key, and attempt to cast to an array of strings - note that this returns an optional, so the type of array is [String]?
In the next line we add a new element to the array - note that if array is nil, the optional chaining expression evaluates to nil, and nothing happens
In the last line, we set the new array value back to the corresponding key - this step is required because the array is a value type, so when we extract it from the dictionary, we actually get a copy of it - so any update made on it won't be applied to the original array.

Related

How can I initialize an OrderedDictionary from a regular Dictionary in Swift?

Apples swift-collections package provides an alternative to a Dictionary type which guarantees to keep (not bring) its key-value pairs in order (unlike the regular Dictionary type). But how can I turn an instance of type Dictionary into an instance of type OrderedDictionary for later re-ordering?
For example, how would it work for this dict:
let regularDict: [String: String] = ["key1": "value1", "key2": "value2"]
// => EITHER {key2=>value2, key1=>value1} OR {key1=>value1, key2=>value2}
let orderedDict: OrderedDictionary<String, String> = .init(/* ??? */)
I am aware that orderedDict will have a random order of key-value entries at first, that's fine for me, I just need an OrderedDictionary instance so any future changes to the order I make don't get lost. I just want to know how to initialize it from a Dictionary in a way that is performant and makes sure the keys stay connected to the correct values.
You can use init(uniqueKeys:values:) with the regular dictionaries' keys and values:
let regularDict = ["key1": "value1", "key2": "value2"]
// => EITHER {key2=>value2, key1=>value1} OR {key1=>value1, key2=>value2}
var orderedDict = OrderedDictionary<String, String>(
uniqueKeys: regularDict.keys,
values: regularDict.values
)
// => EITHER {key2=>value2, key1=>value1} OR {key1=>value1, key2=>value2}
This will not magically sort anything though, the resulting orderedDict will have key-value entries of same random order as the regularDict. But any future changes to the order will be persistent. For example, you could order by keys of a type conforming to Comparable like this:
orderedEntries.sort(by: { $0.key < $1.key })
// => {key1=>value1, key2=>value2}

How to add/Update key/values in dict in swift?

func exercise() {
var stockTickers: [String: String] = [
"APPL" : "Apple Inc",
"HOG": "Harley-Davidson Inc",
"BOOM": "Dynamic Materials",
"HEINY": "Heineken",
"BEN": "Franklin Resources Inc"
]
stockTickers["WORK"] = ["Slack Technologies Inc"]
stockTickers["BOOM"] = ["DMC Global Inc"]
print(stockTickers["WORK"]!)
print(stockTickers["BOOM"]!)
}
Error: Cannot assign value of type '[String]' to subscript of type 'String'
I do not understand the error, I'm new to swift, can someone guide me through this and tell mw why I see this error.
Alexander explained what you need to do in abstract terms. (Voted)
Specifically, change
stockTickers["WORK"] = ["Slack Technologies Inc"]
stockTickers["BOOM"] = ["DMC Global Inc"]
To
stockTickers["WORK"] = "Slack Technologies Inc"
stockTickers["BOOM"] = "DMC Global Inc"
The expression ["Slack Technologies Inc"] defines a String array containing a single string. That's not what you want. you defined a dictionary of type [String:String]
If you wanted your dictionary to have String keys and values that contained arrays of strings, you'd have to change the way you declared your dictionary:
var stockTickers: [String: [String]] = [
"APPL" : ["Apple Inc"],
"HOG": ["Harley-Davidson Inc"],
"BOOM": ["Dynamic Materials"],
"HEINY": ["Heineken"],
"BEN": ["Franklin Resources Inc"]
]
["DMC Global Inc"] is an array literal containing a string. It evaluates to a value of type [String] (a.k.a. Array<String>).
Thus the error makes sense: you’re trying to a assign an array of strings in a place where only a String is expected.
Just remove the square brackets.

Swift cannot append to subscript?

I can't seem to use .append() on a subscript.
For example, here's the array:
var arrayTest = [
"test": 8,
"test2": 4,
"anotherarry": [
"test4": 9
]
]
I am able to do this:
arrayTest.append(["test3": 3])
But I can't append to the array inside arrayTest. This is what I'm trying:
arrayTest["anotherarray"].append(["finaltest": 2])
First note: your variable arrayTest is a dictionary of type [String: NSObject], not an array. Similarly the value for the key anotherarray is also a dictionary.
Second note: you are setting the key anotherarry and retrieving the key anotherarray which would be nil in this example.
I'm also not sure how you are able to call append() on arrayTest since it is a dictionary and doesn't have that method.
But the key issue with what you are trying to do is that dictionaries and arrays are value types and are copied when passed around, rather than referenced. When you subscript arrayTest to get anotherarray, you are getting a copy of the value, not a reference to the value inside the dictionary.
If you want to modify something directly inside an array or dictionary (as opposed to replacing it), that something must be a reference type (a class). Here's an example of how your code could be accomplished:
var arrayTest = [
"test": 8,
"test2": 4,
"anotherarray": ([
"test4": 9
] as NSMutableDictionary)
]
(arrayTest["anotherarray"] as? NSMutableDictionary)?["test5"] = 10
Note that this code forces "anotherarray" to explicitly be an NSMutableDictionary (a class type from Objective-C) instead of defaulting to a Swift dictionary (a value type). That's what makes it possible to modify it from outside the dictionary, since it is now being passed as a reference and not copied.
Further Note:
As pointed out in the comments, using NSMutableDictionary is not something I personally recommend and isn't a pure Swift solution, it's just the way to arrive at a working example with the fewest changes to your code.
Your other options would include replacing the anotherarray value entirely with a modified copy instead of trying to subscript it directly, or if it's important for you to be able to chain your subscripts, you could create a class wrapper around a Swift dictionary like this:
class DictionaryReference<Key:Hashable, Value> : DictionaryLiteralConvertible, CustomStringConvertible {
private var dictionary = [Key : Value]()
var description: String {
return String(dictionary)
}
subscript (key:Key) -> Value? {
get {
return dictionary[key]
}
set {
dictionary[key] = newValue
}
}
required init(dictionaryLiteral elements: (Key, Value)...) {
for (key, value) in elements {
dictionary[key] = value
}
}
}
Then you would use it similarly to the NSMutableDictionary example:
var arrayTest = [
"test": 8,
"test2": 4,
"anotherarray": ([
"test4": 9
] as DictionaryReference<String, Int>)
]
(arrayTest["anotherarray"] as? DictionaryReference<String, Int>)?["test5"] = 10
For example, here's the array:
Nope, arrayTest is NOT an array. It's a dictionary.
I am able to do this...
No you're not. There is no such append method into a dictionary.
The problem
So it looks like you have a dictionary like this
var dict: [String:Any] = [
"test": 8,
"test2": 4,
"anotherdict": ["test4": 9]
]
You want to change the array inside the key anotherdict (yes I renamed your key) in order to add the following key/value pair
"finaltest": 2
Here's the code
if var anotherdict = dict["anotherdict"] as? [String:Int] {
anotherdict["finaltest"] = 2
dict["anotherdict"] = anotherdict
}
Result
[
"test2": 4,
"test": 8,
"anotherdict": ["test4": 9, "finaltest": 2]
]

Swift filter function

I am trying to remove null value for key in dictionary
so I have this kind of data:
let dic = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
let array = [dic,2,3,4]
let jsonResult:[String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": array,"About": NSNull()]
let jsonCleanDictionary = filter(jsonResult, {!($0.1 is NSNull)})
can not understand syntax of above filter function
Do not use NSNull() in swift instead prefer using nil. Further, since its a dictionary adding keys with a null value is pretty useless since dictionaries will return nil if the key doesn't exist. So when checking for null all you have to do is
if let some = dic["key"] as? Value {
// some now contains the value inside dic's key as a value type of Value.
}
Also the filter function works by taking a block which returns a bool so:
dict.filter { (key, value) -> Bool in
// Do stuff to check key and value and return a
// bool which is true if you want that key, value pair to
// appear in the filtered result.
}
In swift closure arguments can get anonymous names if not explicitly return. These names are of the format $0, $1, etc. Now, the filter function takes only parameter specifically the Self.Generator.Element from the CollectionType protocol. For dictionaries this is a tuple containing the key and the value. To access members of unnamed tuples you use .0, .1, .2, etc. depending on the index of the tuple member. So for dictionaries Self.Generator.Element is a tuple containing the key and the value. So $0.1 refers to the value of the key,value pair. Hope this clears this weird syntax up a little bit.

How do I put different types in a dictionary in the Swift Language?

Swift only allows a dictionary to contain a single type.
Here's the definition that is taken from the Swift book:
A dictionary is a container that stores multiple values of the same type
[...]
They differ from Objective-C’s NSDictionary and NSMutableDictionary classes, which can use any kind of object as their keys and values and do not provide any information about the nature of these objects.
If that’s the case then how are we going to create nested dictionaries?
Imagine we have a plist that holds String, Array and Dictionary items in it . If I’m allowed to hold only the same of type of items (either string, array etc.) then how am I going to use different types of items stored in the plist?
How do I put different types in the same dictionary in Swift?
You can achieve plist-like nested structures using Any type for dictionary values which is Swift's somewhat counterpart to Objective-C's id type but can also hold value types.
var response = Dictionary<String, Any>()
response["user"] = ["Login": "Power Ranger", "Password": "Mighty Morfin'"]
response["status"] = 200
EDIT:
Any seems to be better than AnyObject because in the above code response["status"] is of type Swift.Int, while using value type of AnyObject it is __NSCFNumber.
As has been suggested, you can use the Any type to represent a plist dictionary's values. But then how do you work with the data? Cast every value any time you look it up from the dictionary? That's really messy. A better, more type-safe way to model a plist would be to take advantage of Swift's enums, also known as algebraic data types or discriminated unions. They let you specify exactly what types are permitted in the dictionary and avoid ever having to cast. Here's an implementation, explained:
// An atomic (i.e. non-collection) data type in a plist.
enum PListNode {
case PLN_String(String)
case PLN_Integer(Int)
case PLN_Float(Double)
case PLN_Bool(Bool)
case PLN_Date(CFDate)
case PLN_Data(CFData)
}
At the most atomic level, only the above data types may be stored in a plist. Each 'node' in the plist can ultimately can only be one of these types. So we create an enum which lets us specify this.
// A value that can be stored in a plist Dictionary's key-value pair.
enum PListValue {
case PLV_Node(PListNode)
case PLV_Array(PListNode[])
case PLV_Dictionary(Dictionary<String, Box<PListValue>>)
}
typealias PList = Dictionary<String, Box<PListValue>>
A plist is basically a dictionary of key-value pairs, and each value can be either an atomic (i.e. non-collection) value; or it can be an array of atomic values; or it can be a dictionary of string-plist value pairs. The above enum expresses these constraints, and the typealias gives the plist type an easy-to-remember name.
Given the above types, we can completely express any given plist in a type-safe way, e.g.:
// Example translated from
// https://developer.apple.com/library/Mac/documentation/Darwin/Reference/ManPages/man5/plist.5.html
let myPlist: PList = [
"Year Of Birth": Box(PLV_Node(PLN_Integer(1965)))
, "Pets Names": Box(PLV_Array([]))
, "Picture": Box(PLV_Node(PLN_Data(...)))
, "City of Birth": Box(PLV_Node(PLN_String("Springfield")))
, "Name": Box(PLV_Node(PLN_String("John Doe")))
, "Kids Names": Box(
PLV_Array([PLN_String("John"), PLN_String("Kyra")])
)
]
What it means to be type-safe here is that you can process any given plist using a switch statement and cover all possibilities without the need for any casting. You're eliminating a whole class of potential runtime errors. E.g.:
// See https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-XID_189 for explanation
switch myPlist["Year Of Birth"] {
case Box(.PLV_Node(let plvNodeValue)):
...
case Box(.PLV_Array(let plvArrayValue)):
...
case Box(.PLV_Dictionary(let plvDictionaryValue)):
...
}
Note that it's necessary to wrap up recursive data structures in a 'box' (a pointer to the actual value) to keep their sizes finite.
NSObject works for my case while "Any" does not
var d:Dictionary<String,NSObject> = [:]
d["key1"] = "ddd"
d["key2"] = 111 //OK
NSLog("%#", d) //OK
var d2:Dictionary = Dictionary<String,Any>()
d2["key1"] = "ddd"
d2["key2"] = 111
NSLog("%#", d2) //I got error here
Use NSMutableDictionary like this :
var dictInfo : NSMutableDictionary = [ "lang_key": "1"]
dictInfo["food_type"] = lbl_TypeOfFood.text
dictInfo["search_text"] = txt_Search.text
dictInfo["date"] = lbl_Date.text
dictInfo["opening_hours"] = lbl_OpeningHours.text
hope this will work fine .
Use: Dictionary<String, AnyObject>
var dict: Dictionary<String, AnyObject> = [
"number": 1,
"string": "Hello",
]
NSMutableDictionary to Dictionary works like a charm and will allow you to put different types in a Dictionary in the Swift Language:
let nsMutableDictionary = NSMutableDictionary()
nsMutableDictionary[NSFontAttributeName] = UIFont(name: "HelveticaNeue", size: 12.0)!
nsMutableDictionary[NSForegroundColorAttributeName] = UIColor.redColor()
let dictionary: Dictionary<NSObject, AnyObject> = nsMutableDictionary
self.attributedPlaceholder = NSAttributedString(string: textParam, attributes: dictionary)
let dictionary : Dictionary = [
"key": "value",
"key2": 2,
"key3": NSString(),
2: "test",
]
One can specify types which restricts the dictionary
let dictionary : Dictionary<String, String> = [
"key": "value",
"key2": 2, // This errors
]