How to Create Nested Dictionary Elements in Swift

How to create nested dictionary elements in Swift?

Since you explicitly want it as [String:AnyObject]:

var dict: [String:AnyObject] = ["messageCode":"API_200" as AnyObject,
"data": ["activities": [["action":1,
"state":1]],
"messages": [["body":"hi"]]] as AnyObject,
"message": "" as AnyObject]

Basically all the root values should be typecasted as AnyObject

Or the long way:

//Activities is as Array of dictionary with Int values
var activities = [[String:Int]]()
activities.append(["action": 1,
"state": 1])

//Messages is an Array of string
var messages = [[String:String]]()
messages.append(["body" : "hi"])

//Data is dictionary containing activities and messages
var data = [String:Any]()
data["activities"] = activities
data["messages"] = messages

//Finally your base dictionary
var dict = [String:AnyObject]()
dict["messageCode"] = "API_200" as AnyObject
dict["data"] = data as AnyObject
dict["message"] = "" as AnyObject
print(dict)

Parsing this to get your data back will be hell; with all the type casts and all.

Example (lets capture action):

let action = ((dict["data"] as? [String:Any])?["activities"] as? [String:Int])?.first?.value

As you can see you need to typecast at every level. This is the problem with using dictionaries in Swift. Too much cruft.

Sure, you could use a third-party library like SwiftyJSON to reduce the above to:

let action = dict["data"]["activities"][0]["action"]

But do you want a dependency just for something as simple as this?

Instead...

If your structure is defined then create models instead; as Ahmad F's answer suggests. It will be more readable, maintainable and flexible.

...but since you asked, this is how one would do it with pure Dictionary elements.

Write to Nested Dictionary (Swift 4)

You need to instantiate every inner dictionary.

var dict = [String : [String : [String]]]()

dict["Test"] = [String : [String]]()

dict["Test"]?["One"] = ["Worked"]

print(dict)

Make sure to avoid force unwrapping.

How to access deeply nested dictionaries in Swift

When working with dictionaries you have to remember that a key might not exist in the dictionary. For this reason, dictionaries always return optionals. So each time you access the dictionary by key you have to unwrap at each level as follows:

bugsDict["ladybug"]!["spotted"]!["red"]!++

I presume you know about optionals, but just to be clear, use the exclamation mark if you are 100% sure the key exists in the dictionary, otherwise it's better to use the question mark:

bugsDict["ladybug"]?["spotted"]?["red"]?++

Addendum: This is the code I used for testing in playground:

var colorsDict = [String : Int]()
var patternsDict = [String : [String : Int]] ()
var bugsDict = [String : [String : [String : Int]]] ()

colorsDict["red"] = 1
patternsDict["spotted"] = colorsDict
bugsDict["ladybug"] = patternsDict

bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 1
bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 2
bugsDict["ladybug"]!["spotted"]!["red"]!++ // Prints 3
bugsDict["ladybug"]!["spotted"]!["red"]! // Prints 4

Determing dictionary with nested dictionaries type in Swift

Much much better. Never go back ;) Note that:

  • First letter of structs should be capitalised. ( Linter will shoot if you don't. )
  • You can sometimes embed structs, to enforce scoping. It's up to you and depends of the situation.
  • You don't have to use Array<> and Dictionary<> notations. Prefer brackets.
  • Define a 'string' datatype is not really advised, since it already defines a primitive type. Note that you can use `String` between quotes to redefine primitive types in your scope, but it is a bad practice.
struct Data: Hashable {
struct Text: Hashable {
let translation: String
let pinned: Bool
let order: Int
}

struct Language: Hashable {
let language: String
let target: Bool
let texts: [String: Text]
}

let base: String
let target: String
let languages: [Language]
}

If you need to use Text type outside:

var text: Data.Text

And finally, It's a bit curious to define an array of translations in a Language type. Maybe you should name it Translations and structure a bit differently. for example have a Text struct that contains a Translations dict, with language code as the key. Just a suggestion.. Good luck :)

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

Swift: Create Dictionary with keys based on nested array

You should be able to solve this with a double forEach loop

models.forEach { model in 
model.categories.rooms.forEach { cat in
rooms[cat, default: []].append(model)
}
}

Create a property for rooms,

var rooms: [String: [Model]] = [:]

and then add an init to your class

init() {
models = load("models.json")
models.forEach { model in
model.categories.rooms.forEach { cat in
rooms[cat, default: []].append(model)
}
}
}

Note that I am using the string values for categories rather than an enum since the enum code isn't included in the question. So either do cat.rawValue or perhaps even better change the dictionary to have the enum as key.

Convert nested dictionary to create String Swift

With:

struct Item {
let name: String?
let itemId: String?
}

let dict: [String: [Item]] = ["price": [Item(name: "10-25", itemId: "10-25")],
"publisher": [Item(name: "ABCD", itemId: "576"),
Item(name: "DEFG", itemId: "925"),
Item(name: "HIJK", itemId: "1737")]]

You could use:

var keys = ["price", "publisher"]
let reduced = keys.reduce(into: [String]()) { result, current in
guard let items = dict[current] else { return }
let itemsStr = items.compactMap {$0.itemId }.joined(separator: ",")
result.append("\(current):\(itemsStr)")
}
let finalStr = reduced.joined(separator: ";")
print(finalStr)

The idea:
Iterate over the needed keys (and order guaranteed), construct for each keys, the itemsIds list joined by "," and then append that with the key.
Finally, joined all that.

Add-on questions:
Why is name and itemId optional? Is that normal?

Site note: giving the first part (easily reproducible input) can increase the change of answers, so we don't have to recreate ourselves fake data to check our answers.

How to insert values into a nested Swift Dictionary

Dictionarys are value types so are copied on assignment. As a result you are going to have to get the inner dictionary (which will be a copy), add the new key, then re-assign.

// get the nested dictionary (which will be a copy)
var inner:Dictionary<Int, String> = dict[1]!

// add the new value
inner[3] = "three"

// update the outer dictionary
dict[1] = inner
println(dict) // [1: [1: one, 2: two, 3: three]]

You could use one of the new utility libraries such as ExSwift to make this a bit simpler:

dict[1] = dict[1]!.union([3:"three"])

This uses the union method that combines two dictionaries.



Related Topics



Leave a reply



Submit