How to update or add value in nested dictionary swift?
You can update the dictionary value like this
var dataDictionary = Dictionary<String, Any>()
dataDictionary = [
"reference": "",
"country": "",
"language": "EN",
]
dataDictionary["innerObject"] = [
"name": "",
"middle_name" : "",
"address" : "",
]
var obj = dataDictionary["innerObject"] as! [String: Any]
obj["name"] = "Your name"
dataDictionary["innerObject"] = obj
print(dataDictionary) //["reference": "", "language": "EN", "country": "", "innerObject": ["name": "Your name", "address": "", "middle_name": ""]]
How to update a value in a nested dictionary given path fragment in Swift?
Note that var d: [String: Any] = data[first] as! [String: Any]
makes a copy of data[first]
and not a reference to it like &
and inout
. So, when addAt(pathFrag: &pathFrag, string: string, data: &d)
is called, only d
is changed. To make this change reflect on the original data[first]
, you have to assign d
back to data[first]
.
Do this:
var dict: [String: Any] = ["channel": ["item": ["title": 1111]]]
var pathFrag = ["channel", "item", "title"]
func addAt(pathFrag: inout [String], data: inout [String: Any]) {
if let first = pathFrag.first {
if let item = data[first] {
pathFrag.remove(at: 0)
if !pathFrag.isEmpty {
var d: [String: Any] = data[first] as! [String: Any]
addAt(pathFrag: &pathFrag, data: &d)
data[first] = d
} else {
data[first] = 123
}
}
}
}
addAt(pathFrag: &pathFrag, data: &dict)
print(dict)
Changing value in nested dictionary in swift
It's because dictionaries are value types and not reference types in Swift.
When you call this line...
var dic2 = dic["key"] as Dictionary<String, String>
... you are creating an entirely new dictionary, not a reference to the value in dic
. Changes in dic2
will not be reflected in dic
, because dic2
is now a second entirely separate dictionary. If you want dic
to reflect the changes that you made, you need to reassign the value to the appropriate key in dic
, like this:
dic["key"] = dic2
Hope that helps...
Swift replace key value in array of dictionaries with nested dictionaries
Here is a solution with a recursive function that replaces all values for a given key.
func update(_ dict: [String: Any], set value: Any, for key: String) -> [String: Any] {
var newDict = dict
for (k, v) in newDict {
if k == key {
newDict[k] = value
} else if let subDict = v as? [String: Any] {
newDict[k] = update(subDict, set: value, for: key)
} else if let subArray = v as? [[String: Any]] {
var newArray = [[String: Any]]()
for item in subArray {
newArray.append(update(item, set: value, for: key))
}
newDict[k] = newArray
}
}
return newDict
}
Note that it doesn't check what type the existing value is but directly replaces it with the new value. Also the code assumes the only types of nested arrays are arrays of dictionaries.
For the array this function can be used with map
let out = data.map { update($0, set: "newValue", for: "id")}
Update dictionary values in nested dictionary
You can try this -
func replaceErrorWithCustomError(data: inout [String: Any]) {
func updateError(dict: inout [String: Any]) -> [String: Any] {
for (key, value) in dict {
if key == "key" {
dict.updateValue("abc", forKey: key)
break
} else if var value = value as? [String: Any] {
// This is the key change
// Result must be updated back into parent
dict[key] = updateError(dict: &value)
}
}
return dict
}
updateError(dict: &data)
}
Can't update value of nested Dictionary
This is a bit convoluted but it works:
class Test {
var methods = [String: [String: AnyObject]]()
let defaultParams = ["Offset": "0 min", "Midnight": "Standard"]
init() {
methods["Punjab"] = [
"name": "Punjab Time",
"params": ["FJ": 18, "IS": 17]
]
methods["Karachi"] = [
"name": "Time of Karachi",
"params": ["FJ": 15, "IS": 15]
]
methods["Lahore"] = [
"name": "Lahore City Time",
"params": ["FJ": 19.5, "IS": 17.5 , "MG": 8.9]
]
for (methodName, methodValue) in methods {
for (mKey, mValue) in methodValue {
if mKey == "params" {
var pDic = mValue as! [String: AnyObject]
for (defaultKey, defaultValue) in defaultParams {
if !contains(pDic.keys, defaultKey) {
pDic[defaultKey] = defaultValue
methods[methodName]!["params"]! = pDic
}
}
}
}
}
}
}
let t = Test()
println(t.methods)
Result:
[Karachi: [params: {
FJ = 15;
IS = 15;
Midnight = Standard;
Offset = "0 min";
}, name: Time of Karachi], Punjab: [params: {
FJ = 18;
IS = 17;
Midnight = Standard;
Offset = "0 min";
}, name: Punjab Time], Lahore: [params: {
FJ = "19.5";
IS = "17.5";
MG = "8.9";
Midnight = Standard;
Offset = "0 min";
}, name: Lahore City Time]]
Swift - Update Value of a specific item in a nested dictionary
I worked out i was updating the main dictionary array and not the nested array.
The code below is what I used in the end.
let singleAction = [linkedButtonUUID: connectorMarker.UUIDpic.uuidString, linkedButtonCategory: "", linkedButtonName: connectorMarker.name]
var mainMarkerAction = mainMarker.buttonAction["button actions array"]!
for existingMarker in mainMarkerAction {
actionArray.append(existingMarker)
}
var actionSub = actionArray[indexRowTag].array_linked_of_buttons
if let addAction = actionSub.filter({$0[linkedButtonUUID] == connectorMarker.UUIDpic.uuidString}).first {
print("MARKER HERE", addAction)
} else {
actionArray[indexRowTag].array_linked_of_buttons.append(singleAction)
}
mainMarkerAction[indexRowTag] = actionArray[indexRowTag]
Swift map nested dictionary to swap outer and inner keys
A possible way is to use reduce(into:_:)
:
With sample:
let rates: [String: [String: Double]] = ["2021-11-14": ["CAD": 1.1,
"USD": 1.0,
"EUR": 0.9],
"2021-11-15": ["CAD": 1.11,
"USD": 1.01,
"EUR": 0.91]]
This should do the trick:
let target = rates.reduce(into: [String: [String: Double]]()) { partialResult, current in
let date = current.key
let dayRates = current.value
dayRates.forEach { aDayRate in
var currencyRates = partialResult[aDayRate.key, default: [:]]
currencyRates[date] = aDayRate.value
partialResult[aDayRate.key] = currencyRates
}
}
For the logic, we iterate over each elements of rates
.
For each [CurrencyCode: Amount], we iterate on them, and set them to partialResult
(which at the end of the inner loop of reduce(into:_:)
will be the finalResult
, ie the returned value).
Outputs for print(rates)
& print(target)
(I just formatted them to make them easier to read):
$> ["2021-11-14": ["CAD": 1.1, "EUR": 0.9, "USD": 1.0],
"2021-11-15": ["USD": 1.01, "EUR": 0.91, "CAD": 1.11]]
$> ["EUR": ["2021-11-14": 0.9, "2021-11-15": 0.91],
"USD": ["2021-11-15": 1.01, "2021-11-14": 1.0],
"CAD": ["2021-11-14": 1.1, "2021-11-15": 1.11]]
Filter a dictionary with a nested dictionary in Swift 5
To give you some examples how to filter I created a set of data from the winner and runner up in the Champions League tournament. In the main dictionary the Int
key correspond to the tournament's year. The dictionary inside has two keys: 1stPlace
and 2ndPlace
. So for example in 2018 Real Madrid defeated Liverpool, the dictionary will look like this:
2018: ["1stPlace": "Real Madrid", "2ndPlace": "Liverpool"]
Assuming this structure the Data
variable has the results from 2018 to 2011. If you want to know who won in 2018 you can do something like this:
filteredData = Data.filter { $0.key == 2018 }
If you want know when Real Madrid won you doo like this:
filteredData = Data.filter { $0.value["1stPlace"] == "Real Madrid" }
If you want to know when Real Madrid defeated Atlético Madrid you do it like this:
filteredData = Data.filter {
$0.value["2ndPlace"] == "Atlético Madrid" &&
$0.value["1stPlace"] == "Real Madrid"
}
When you have the results in filteredData
you can do query the data in different ways: you can sort by year, you can extract just the years, etc.
The following code is the playground I used to test different kinds of queries:
import Foundation
typealias DictOfDicts = [Int : [String : String]]
var Data: DictOfDicts = [
2018:
["1stPlace": "Real Madrid", "2ndPlace": "Liverpool"],
2017:
["1stPlace": "Real Madrid", "2ndPlace": "Juventus"],
2016:
["1stPlace": "Real Madrid", "2ndPlace": "Atlético Madrid"],
2015:
["1stPlace": "Barcelona", "2ndPlace": "Juventus"],
2014:
["1stPlace": "Real Madrid", "2ndPlace": "Atlético Madrid"],
2013:
["1stPlace": "Bayern Munich", "2ndPlace": "Borussia Dortmund"],
2012:
["1stPlace": "Chelsea", "2ndPlace": "Bayern Munich"],
2011:
["1stPlace": "Barcelona", "2ndPlace": "Manchester United"]
]
var filteredData: DictOfDicts = [:]
print("Results from 2017 to present")
print("----------------------------")
filteredData = Data.filter { $0.key >= 2017 } // Filter from 2017 to present
filteredData
.sorted { $0.key > $1.key } // Sort by year
.forEach {
print("In \($0.key) \($0.value["1stPlace"]!) defeated \($0.value["2ndPlace"]!)")
}
print("")
print("Results before 2015")
print("----------------------------")
filteredData = Data.filter { $0.key < 2015 } // Filter before 2015
filteredData
.sorted { $0.key > $1.key } // Sort by year
.forEach {
print("In \($0.key) \($0.value["1stPlace"]!) defeated \($0.value["2ndPlace"]!)")
}
print("")
filteredData = Data.filter { $0.value["1stPlace"] == "Real Madrid" } // Filter Real Madrid won
var years = filteredData
.sorted { $0.key < $1.key } // Sort by year
.map { "\($0.key)" } // Convert year to string
.joined(separator: ", ") // Convert to comma separated single string
print("Real Madrid won in: \(years)")
print("")
filteredData = Data.filter { $0.value["2ndPlace"] == "Juventus" } // Filter Juventus lost
years = filteredData
.sorted { $0.key > $1.key } // Sort by year
.map { "\($0.key)" } // Convert year to string
.joined(separator: ", ") // Convert to comma separated single string
print("Juventus lost the final match in: \(years)")
print("")
filteredData = Data.filter {
$0.value["2ndPlace"] == "Atlético Madrid" &&
$0.value["1stPlace"] == "Real Madrid"
} // Filter Real Madrid defeated Atlético Madrid
years = filteredData
.sorted { $0.key > $1.key } // Sort by year
.map { "\($0.key)" } // Convert year to string
.joined(separator: ", ") // Convert to comma separated single string
print("Real Madrid defeated Atlético Madrid in: \(years)")
print()
let winnersAndChampionships =
Set(Data.map { $0.value["1stPlace"]! }) // Get winners' names
.map { winner in
(winner, Data.filter { $0.value["1stPlace"] == winner }.count)
} // Map for each winner the number of wins
.sorted { $0.1 > $1.1}
print("Number of Champions League's wins per team")
print("------------------------------------------")
winnersAndChampionships.forEach {
print("\($0.0): \($0.1)")
}
The playground's output is:
Results from 2017 to present
----------------------------
In 2018 Real Madrid defeated Liverpool
In 2017 Real Madrid defeated Juventus
Results before 2015
----------------------------
In 2014 Real Madrid defeated Atlético Madrid
In 2013 Bayern Munich defeated Borussia Dortmund
In 2012 Chelsea defeated Bayern Munich
In 2011 Barcelona defeated Manchester United
Real Madrid won in: 2014, 2016, 2017, 2018
Juventus lost the final match in: 2017, 2015
Real Madrid defeated Atlético Madrid in: 2016, 2014
Number of Champions League's wins per team
------------------------------------------
Real Madrid: 4
Barcelona: 2
Bayern Munich: 1
Chelsea: 1
Related Topics
How to Use Objective-C Enum in Swift
Argument for Generic Parameter Could Not Be Inferred
Can You Give Uistackview Borders
Difference Between Awakefromnib() and Viewdidload() in Swift
Swift Error: Missing Argument Label 'Name:' in Call
Reversing the Order of a String Value
How to Detect a 'Click' Gesture in Swiftui Tvos
How to Get Data from Observedobject with Onreceive in Swiftui
Swift: How to Access in Appdelegate Variable from the View Controller
Failed to Get Descriptors for Extensionbundleid
Add Custom Header to Collection View Swift
Weak VS Unowned in Swift. What Are the Internal Differences
Swift: Popover Dismiss Callback
Error When Using Generic as Property Type in Swift
Why Does Swift Return an Unexpected Pointer When Converting an Optional String into an Unsafepointer