Changing Value in Nested Dictionary in Swift

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



Leave a reply



Submit