How can a sum multiple values from the same key across different dictionaries in swift? - swift

I am new to Swift and iOS dev. in general. I was wondering how I could sum multiple values from the same key across different dictionaries. e.g. I have
20 dictionaries with the same key value pair [String: AnyObject] e.g. "Height": 20 I want to sum these and calculate the average.
EG:
// Example dictionary
let player17: [String: AnyObject] = [
"Name": "Les Clay",
"Height": 42,
"Soccer Experience": true,
"Guardian Name(s)": "Wynonna Brown"
]
//Used any object here as they all go into a big array

You can use reduce to add numbers up, like this:
let totalHeight = allPlayers.reduce(0) { (p, c) -> Int in
return p + (c["Height"] as! Int)
}
Note: The c["Height"] as! Int approach requires hard knowledge that "Height" key is present in every dictionary, and its value is of type Int. Otherwise, this will produce an exception at run time.
If some of your dictionaries do not have the proper key in them, or contain the value of a different type, you need to pre-filter or use an optional cast, for example
return p + ((c["Height"] as? Int) ?? 0)

Given
let players: [[String:AnyObject]] = ...
here's another approach
let sum = players.flatMap { $0["Height"] as? Int }.reduce(0, combine: +)
If a dictionary doesn't have a valid Int value for the "Height" key then that dictionary is ignored.

Related

Getting values out of dictionaries which have arrays as values

I have a dictionary which contains languages as values and the initial character of every language name (A, B, C, ...) as for the key.
var dictionary = [Character: [Language]]()
I would like to get all the languages out of the dictionary in the form of an array. To do so, I do
let languages = dictionary.values // Dictionary<Character, [Language]>.Values
That's not an array. I try to get the array like this
let languages = Array(tableViewSource.values) // [[Language]]
This returns an array of an array. How do I just get an array of languages? I saw the merge(_:uniquingKeysWith:) but I don't need to merge dictionaries.
You can try
let allLangs = nestedArr.reduce([], +)
Martin R's and Sh_Khan's answers outline the standard ways of doing this (you basically just want to flatten an array). However I'd like to show a more "manual" approach:
var langs = [String]()
for lang_list in dictionary.values {
langs.append(contentsOf: lang_list)
}
Alternatively, you can use the += operator instead of .append(contentsOf:). An easier way of doing this would be using flatMap(_:):
dictionary.values.flatMap { $0 }
If you want a single array (concatenating all dictionary values) then
Array(dictionary.values.joined())
does the trick. (This creates the final array without creating any additional intermediate arrays.)
Example:
let dictionary: [Character: [String]] = ["E": ["espanol", "english"], "G": ["german", "greek"]]
let langs = Array(dictionary.values.joined())
print(langs) // ["german", "greek", "espanol", "english"]
Note that the order of key/value pairs in a dictionary is unspecified.
An alternative is
let dictionary: [Character: [String]] = ["E": ["espanol", "english"], "G": ["german", "greek"]]
let langs = dictionary.keys.sorted().flatMap { dictionary[$0]! }
print(langs) // ["espanol", "english", "german", "greek"]
which gives the languages sorted by the corresponding keys.

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

<= Operand asking for expected type in Swift?

I'm trying to look for items equal or less than zero in my query like so.....
for zeroItems in snapshot.value as! NSDictionary where zeroItems.value["Value"] as! Int <= 0
I'm getting an Expected type error. Please explain and show me how to correct.
You may want to consider applying a filter to simplify the logic to only include elements less than zero. I've created a working example in the IBM Swift Sandbox.
import Foundation
// Construct the data structure with demo data
struct Snapshot {
let value: NSDictionary
}
let dict: NSDictionary = [ "a" : -1, "b" : 0, "c" : 1]
let snapshot = Snapshot(value: dict)
//Apply a filter
let zeroItems = snapshot.value.filter{Int($0.1 as! NSNumber) <= 0}
//View the result
print(zeroItems)
Almost certainly you've overused Any if you have to use this many as! casts in a single line. Create a custom struct for this type and convert the data to that. If you're passing around NSDictionary and using as! very much, you're fighting the system.
That said, to make this work you probably just need more parentheses. Something like:
for zeroItems in (snapshot.value as! NSDictionary) where (zeroItems.value["Value"] as! Int) <= 0
But this is horrible Swift, so avoid this if possible.
You interpreting snapshot.value as Dictionary, so zeroItems is a tuple of key and value.
As I understand you having array of dictionaries, and you want to filter them by "Value" key, right?
If so then you may use following code:
// dictionary with Value key, and Int value
let dict: [String: Any] = ["Value":-1]
// array of dictionaries
let value: [[String: Any]] = [dict, <**add here as many as you want**>]
// get only array of integeres
let onlyIntegers = value.flatMap { $0["Value"] as? Int }.filter {$0 <= 0 }
print(onlyIntegers)
// get array of dictionaries which passes <=0 check
let onlyLessOrEqualTo0 = value.filter { dict in
if let value = dict["Value"] as? Int {
return value <= 0
}
return false
}
print(onlyLessOrEqualTo0)
First of all, adding some parentheses will help in seeing where the problem is:
for zeroItems in myDict as! NSDictionary where (zeroItems.value["Value"] as! Int) <= 0
Now, we get "Type 'Any' has no subscript members". So the problem is that you haven't told Swift what the type is of zeroItems's value, which I think is a dictionary of , judging from your code:
This compiles for me:
for zeroItems in myDict as! Dictionary<String, Dictionary<String, Int>> where zeroItems.value["Value"]! <= 0
However, this is not pretty. You could probably get more readable code using filter as was suggested in another answer.

How to update dictionary value in Swift

How to
get and
update
the specific element from the array in the dictionary?
var currency : [String: [Double]] = [
"EUR": [1,1],
"USD": [1.06,1.05],
"CHF": [1.07, 1.06]
]
I'm working on currency converter and I need to get eg. the second value (buy rate, in this case: "1.05") to make calculations.
let eur_exchangeRate = currency["EUR"]
This will give you the corresponding array of doubles from the dictionary.
let buyRate = eur_exchangeRate![1]
This will give you the second element in the array of doubles (which, I believe is the buy rate you're wanting to access)
currency["EUR"]![1] = 99
This will set your buy rate for EUROS to 99.
Please look at #vadian's comment on your question to get an idea of a cleaner implementation of these sort.
My tested solution in swift 4.2 for dictionary like [String : [String]]
yourDict![existingKey]![positionOfValueToUpdate] = newValue
example:
var myDict = [keyOne : ["valueA","valueB","valueC","valueD"]
let newValue = "valueX"
myDict![keyOne]![2] = newValue
after this myDict will be:
myDict = [keyOne : ["valueA","valueB","valueX","valueD"]

How do I add more items to this type of name value pair array? [duplicate]

I have a simple Dictionary which is defined like:
var dict : NSDictionary = [ 1 : "abc", 2 : "cde"]
Now I want to add an element into this dictionary: 3 : "efg"
How can I append 3 : "efg" into this existing dictionary?
You're using NSDictionary. Unless you explicitly need it to be that type for some reason, I recommend using a Swift dictionary.
You can pass a Swift dictionary to any function expecting NSDictionary without any extra work, because Dictionary<> and NSDictionary seamlessly bridge to each other. The advantage of the native Swift way is that the dictionary uses generic types, so if you define it with Int as the key and String as the value, you cannot mistakenly use keys and values of different types. (The compiler checks the types on your behalf.)
Based on what I see in your code, your dictionary uses Int as the key and String as the value. To create an instance and add an item at a later time you can use this code:
var dict = [1: "abc", 2: "cde"] // dict is of type Dictionary<Int, String>
dict[3] = "efg"
If you later need to assign it to a variable of NSDictionary type, just do an explicit cast:
let nsDict = dict as! NSDictionary
And, as mentioned earlier, if you want to pass it to a function expecting NSDictionary, pass it as-is without any cast or conversion.
you can add using the following way and change Dictionary to NSMutableDictionary
dict["key"] = "value"
I know this might be coming very late, but it may prove useful to someone.
So for appending key value pairs to dictionaries in swift, you can use updateValue(value: , forKey: ) method as follows :
var dict = [ 1 : "abc", 2 : "cde"]
dict.updateValue("efg", forKey: 3)
print(dict)
SWIFT 3 - XCODE 8.1
var dictionary = [Int:String]()
dictionary.updateValue(value: "Hola", forKey: 1)
dictionary.updateValue(value: "Hello", forKey: 2)
dictionary.updateValue(value: "Aloha", forKey: 3)
So, your dictionary contains:
dictionary[1: Hola, 2: Hello, 3: Aloha]
If your dictionary is Int to String you can do simply:
dict[3] = "efg"
If you mean adding elements to the value of the dictionary a possible solution:
var dict = Dictionary<String, Array<Int>>()
dict["key"]! += [1]
dict["key"]!.append(1)
dict["key"]?.append(1)
Swift 3+
Example to assign new values to Dictionary. You need to declare it as NSMutableDictionary:
var myDictionary: NSMutableDictionary = [:]
let newValue = 1
myDictionary["newKey"] = newValue
print(myDictionary)
For whoever reading this for swift 5.1+
// 1. Using updateValue to update the given key or add new if doesn't exist
var dictionary = [Int:String]()
dictionary.updateValue("egf", forKey: 3)
// 2. Using a dictionary[key]
var dictionary = [Int:String]()
dictionary[key] = "value"
// 3. Using subscript and mutating append for the value
var dictionary = [Int:[String]]()
dictionary[key, default: ["val"]].append("value")
In Swift, if you are using NSDictionary, you can use setValue:
dict.setValue("value", forKey: "key")
Given two dictionaries as below:
var dic1 = ["a": 1, "c": 2]
var dic2 = ["e": 3, "f": 4]
Here is how you can add all the items from dic2 to dic1:
dic2.forEach {
dic1[$0.key] = $0.value
}
Dict.updateValue updates value for existing key from dictionary or adds new new key-value pair if key does not exists.
Example-
var caseStatusParams: [String: AnyObject] = ["userId" : UserDefault.userID ]
caseStatusParams.updateValue("Hello" as AnyObject, forKey: "otherNotes")
Result-
▿ : 2 elements
- key : "userId"
- value : 866
▿ : 2 elements
- key : "otherNotes"
- value : "Hello"
[String:Any]
For the fellows using [String:Any] instead of Dictionary below is the extension
extension Dictionary where Key == String, Value == Any {
mutating func append(anotherDict:[String:Any]) {
for (key, value) in anotherDict {
self.updateValue(value, forKey: key)
}
}
}
As of Swift 5, the following code collection works.
// main dict to start with
var myDict : Dictionary = [ 1 : "abc", 2 : "cde"]
// dict(s) to be added to main dict
let myDictToMergeWith : Dictionary = [ 5 : "l m n"]
let myDictUpdated : Dictionary = [ 5 : "lmn"]
let myDictToBeMapped : Dictionary = [ 6 : "opq"]
myDict[3]="fgh"
myDict.updateValue("ijk", forKey: 4)
myDict.merge(myDictToMergeWith){(current, _) in current}
print(myDict)
myDict.merge(myDictUpdated){(_, new) in new}
print(myDict)
myDictToBeMapped.map {
myDict[$0.0] = $0.1
}
print(myDict)
To add new elements just set:
listParameters["your parameter"] = value
There is no function to append the data in dictionary. You just assign the value against new key in existing dictionary. it will automatically add value to the dictionary.
var param = ["Name":"Aloha","user" : "Aloha 2"]
param["questions"] = "Are you mine?"
print(param)
The output will be like
["Name":"Aloha","user" : "Aloha 2","questions" : ""Are you mine"?"]
To append a new key-value pair to a dictionary you simply have to set the value for the key. for eg.
// Initialize the Dictionary
var dict = ["name": "John", "surname": "Doe"]
// Add a new key with a value
dict["email"] = "john.doe#email.com"
print(dict)
Output -> ["surname": "Doe", "name": "John", "email": "john.doe#email.com"]
var dict = ["name": "Samira", "surname": "Sami"]
// Add a new enter code herekey with a value
dict["email"] = "sample#email.com"
print(dict)
Up till now the best way I have found to append data to a dictionary by using one of the higher order functions of Swift i.e. "reduce". Follow below code snippet:
newDictionary = oldDictionary.reduce(*newDictionary*) { r, e in var r = r; r[e.0] = e.1; return r }
#Dharmesh In your case, it will be,
newDictionary = dict.reduce([3 : "efg"]) { r, e in var r = r; r[e.0] = e.1; return r }
Please let me know if you find any issues in using above syntax.
Swift 5 happy coding
var tempDicData = NSMutableDictionary()
for temp in answerList {
tempDicData.setValue("your value", forKey: "your key")
}
I added Dictionary extension
extension Dictionary {
func cloneWith(_ dict: [Key: Value]) -> [Key: Value] {
var result = self
dict.forEach { key, value in result[key] = value }
return result
}
}
you can use cloneWith like this
newDictionary = dict.reduce([3 : "efg"]) { r, e in r.cloneWith(e) }
if you want to modify or update NSDictionary then
first of all typecast it as NSMutableDictionary
let newdictionary = NSDictionary as NSMutableDictionary
then simply use
newdictionary.setValue(value: AnyObject?, forKey: String)