Swift dictionary, a key with multiple values - swift

I would like to know how I can make a key of a dictionary have multiple values according to the data that comes to it.
Attached basic example:
var temp = [String: String] ()
temp ["dinningRoom"] = "Table"
temp ["dinningRoom"] = "Chair"
In this case, I always return "Chair", the last one I add, and I need to return all the items that I am adding on the same key.
In this case, the "dinningRoom" key should have two items that are "Table" and "Chair".

You can use Swift Tuples for such scenarios.
//Define you tuple with some name and attribute type
typealias MutipleValue = (firstObject: String, secondObject: String)
var dictionary = [String: MutipleValue]()
dictionary["diningRoom"] = MutipleValue(firstObject: "Chair", secondObject: "Table")
var value = dictionary["diningRoom"]
value?.firstObject

You can declare a dictionary whose value is an array and this can contain the data you want, for example:
var temp = [String: [String]]()
temp["dinningRoom"] = ["Table", "Chair", "Bottle"]
If you want to add a new element you can do it this way:
if temp["dinningRoom"] != nil {
temp["dinningRoom"]!.append("Flower")
} else {
temp["dinningRoom"] = ["Flower"]
}
Now temp["dinningRoom"] contains ["Table", "Chair", "Bottle", "Flower"]

Use Dictionary like this:
var temp = [String: Any]()
temp["dinningRoom"] = ["Table", "Chair"]
If you want to fetch all the elements from dinningRoom. You can use this:
let dinningRoomArray = temp["dinningRoom"] as? [String]
for room in dinningRoomArray{
print(room)
}
It is not compiled code but I mean to say that we can use Any as value instead of String or array of String. When you cast it from Any to [String]
using as? the app can handle the nil value.

Related

Getting Array value in a dictionary swift

I am trying to get key and value in a Dictionary while I am able to the key and map it to a dictionary, I am unable to get the value which is an array.
var dict = [String: [String]]
I was able to get the key as an array which is what I want like:
var keyArray = self.dict.map { $0.key }
How can I get the value which is already an array
Use flatMap if you want to flatten the result you get when you use map which is of type [[String]].
let valueArray = dict.flatMap { $0.value } // gives `[String]` mapping all the values
Here is how you get each string count Array
var dict = [String: [String]]()
let countOfEachString = dict.map { $0.value }.map{ $0.count }
Each value is a string array .. so to access each array you need to use map again

How to extract a subset of a swift 3 Dictionary

I've looked through the methods here but I can't quite find what I'm looking for. I'm new-ish to Swift. I would like to extract a subset from a Dictionary based on a Set of key values, preferably without a loop.
For example, if my key Set is of type Set<String> and I have a Dictionary of type Dictionary<String, CustomObject>, I would like to create a new Dictionary of type Dictionary<String, CustomObject> that contains only the key-value pairs associated with the keys in the Set of Strings.
I can see that I could do this with for loop, by initializing a new Dictionary<String, CustomObj>(), checking if the original Dictionary contains a value at each String in the set, and adding key-value pairs to the new Dictionary. I am wondering if there is a more efficient/elegant way to do this however.
I'd be open to finding the subset with an Array of Strings instead of a Set if there is a better way to do it with an Array of keys.
Many thanks!
Swift 5 - You can do this very simply:
let subsetDict = originalDict.filter({ mySet.contains($0.key)})
The result is a new dictionary with the same type as the original but which only contains the key-value pairs corresponding to the keys in mySet.
Your assumption is correct, there is a more concise/swift-ish way to accomplish what you need.
For example you can do it via reduce, a functional programming concept available in Swift:
let subDict = originalDict.reduce([String: CustomObject]()) {
guard mySet.contains($1.key) else { return $0 }
var d = $0
d[$1.key] = $1.value
return d
}
Or, in two steps, first filtering the valid elements, and then constructing back the dictionary with the filtered elements:
let filteredDict = originalDict.filter { mySet.contains($0.key) }
.reduce([CustomObject]()){ var d = $0; d[$1.key]=$1.value; return d }
forEach can also be used to construct the filtered dictionary:
var filteredDict = [CustomObject]()
mySet.forEach { filteredDict[$0] = originalDict[$0] }
, however the result would be good it it would be immutable:
let filteredDict: [String:CustomObject] = {
var result = [String:CustomObject]()
mySet.forEach { filteredDict2[$0] = originalDict[$0] }
return result
}()
Dummy type:
struct CustomObject {
let foo: Int
init(_ foo: Int) { self.foo = foo }
}
In case you'd like to mutate the original dictionary (instead of creating a new one) in an "intersect" manner, based on a given set of keys:
let keySet = Set(["foo", "baz"])
var dict = ["foo": CustomObject(1), "bar": CustomObject(2),
"baz": CustomObject(3), "bax": CustomObject(4)]
Set(dict.keys).subtracting(keySet).forEach { dict.removeValue(forKey: $0) }
print(dict) // ["foo": CustomObject(foo: 1), "baz": CustomObject(foo: 3)]

Swift 3: Change item in dictionary

I'm saving lists in a dictionary. These lists need to be updated. But when searching for an item, I need [] operator. When I save the result to a variable, a copy is used. This can not be used, to change the list itself:
item = dicMyList[key]
if item != nil {
// add it to existing list
dicMyList[key]!.list.append(filename)
// item?.list.append(filename)
}
I know, that I need the uncommented code above, but this accesses and searches again in dictionary. How can I save the result, without searching again? (like the commented line)
I want to speed up the code.
In case you needn't verify whether the inner list was actually existing or not prior to adding element fileName, you could use a more compact solution making use of the nil coalescing operator.
// example setup
var dicMyList = [1: ["foo.sig", "bar.cc"]] // [Int: [String]] dict
var key = 1
var fileName = "baz.h"
// "append" (copy-in/copy-out) 'fileName' to inner array associated
// with 'key'; constructing a new key-value pair in case none exist
dicMyList[key] = (dicMyList[key] ?? []) + [fileName]
print(dicMyList) // [1: ["foo.sig", "bar.cc", "baz.h"]]
// same method used for non-existant key
key = 2
fileName = "bax.swift"
dicMyList[key] = (dicMyList[key] ?? []) + [fileName]
print(dicMyList) // [2: ["bax.swift"], 1: ["foo.sig", "bar.cc", "baz.h"]]
Dictionaries and arrays are value types. So if you change an entry you'll need to save it back into the dictionary.
if var list = dicMyList[key] {
list.append(filename)
dicMyList[key] = list
} else {
dicMyList[key] = [filename]
}
It's a little bit late, but you can do something like this:
extension Optional where Wrapped == Array<String> {
mutating func append(_ element: String) {
if self == nil {
self = [element]
}
else {
self!.append(element)
}
}
}
var dictionary = [String: [String]]()
dictionary["Hola"].append("Chau")
You can try this in the Playground and then adapt to your needs.

Adding multiple values into a single key in swift?

When I try to add multiple values to the key, my value gets overridden by the last value I assigned to the key. I tried adding brackets between the value String of my [String: [String]] key-value pair. I did that to hopefully achieve the effect to add multiple values into that key.
import UIKit
var parent = [String: [String]]()
parent["parent"] = ["Tommy Turner", "Wolfgang Motart"]
parent["parent"] = ["Bobby Bushe"]
print(parent)
// How can I add multiple values into the parent key like this:
// ["parent": "Tommy Turner", "Wolgang Motart", "Bobby Bushe"]
Since the type of parent["parent"] is array of string, you can use append function for adding one or multiple elements. Try this.
var parent = [String: [String]]()
parent["parent"] = ["Tommy Turner", "Wolfgang Motart"]
parent["parent"]?.append("bob") // append one element to become ["Tommy Turner", "Wolfgang Motart", "bob"]
parent["parent"]?.appendContentsOf(["hello", "world"]) // append collection
print(parent["parent"]!) // ["Tommy Turner", "Wolfgang Motart", "bob", "hello", "world"]
You should add new array to previous like:
var parent = [String: [String]]()
parent["parent"] = ["Tommy Turner", "Wolfgang Motart"]
parent["parent"] = (parent["parent"] ?? []) + ["Bobby Bushe"]
print(parent)
why not combine the [string] and assign to the key:
var parent = [String: [String]]()
let value = ["Tommy Turner", "Wolfgang Motart"] + ["Bobby Bushe"]
parent["parent"] = value
print(parent) // ["parent": ["Tommy Turner", "Wolfgang Motart", "Bobby Bushe"]]
You are overriding the key of "parent" in this key-value pairing. If you want it to have those three names, just put all three names in the array.
parent["parent"] = ["Name", "name", "nombre"]
If you were to create an array outside of scope, and set the value for "parent"
var namesArray = ["Name", "name"]
parent["parent"] = namesArray
namesArray.append("nombre")
It would still only print out ["Name", "name"]. However, if you were to call
parent["parent"] = namesArray
again, it would create what you want.

accessing inner dictionaries (dictionary of dictionaries) with one entry, when not knowing what key or value for that entry are

This is how I declared my dictionary:
var dataDictionary: [NSIndexPath:[Int:Bool]]!
The inner dictionary will always have just one entry e.g. [1:true]. When I search dataDictionary I specify a NSIndexPath. The inner dictionary gets returned. Sth like this:
var innerDictionary = dataDictionary[indexPath] // is of type [Int:Bool]
I would like to access inner dictionarys key and value, but I don't want to specify an Integer or Bool, because it is unknown. I only want to get that Int and/or Bool value stored in inner dictionary. No search of inner dictionary is needed, because if will always contains just one entry. Maybe something like this:
var key = innerDictionary.key
var value = innerDictionary.value
How can this be accomplished?
If your dictionary has only one entry, and you don't know the key, with Swift 2 you can get the first element of the keys sequence safely with if let:
let innerDictionary = [42: true]
if let k = innerDictionary.keys.first {
print(k) // prints 42
} else {
// dict is empty
}
Same for the value:
if let v = innerDictionary.values.first {
print(v) // prints true
} else {
// key has no value
}
Try by this way:
func nestedDic(){
var innerDictionary:NSDictionary = ["Item 1": "data 0", "Item 2": "data 1"]
//NSLog("original object:\(innerDictionary)")
NSLog("all keys array:\(innerDictionary.allKeys)")
if(innerDictionary.isKindOfClass(NSDictionary)){
for key in innerDictionary.allKeys{
let value = innerDictionary.valueForKey(key as! NSString as String)
NSLog("key value = \(value as! String)")
}
}
}
You could create a simple struct for the inner dictionary if there is only one entry, then you are able to use your favorite property names.
struct InnerDictionary {
var key = 0
var value = false
}
let indexPath = NSIndexPath(index: 1)
var dataDictionary = [indexPath: InnerDictionary(key: 1, value: true)]
if let innerDictionary = dataDictionary[indexPath] {
var key = innerDictionary.key
var value = innerDictionary.value
}