Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
652 views
in Technique[技术] by (71.8m points)

ios - Remove nested key from dictionary

Let's say I have a rather complex dictionary, like this one:

let dict: [String: Any] = [
    "countries": [
        "japan": [
            "capital": [
                "name": "tokyo",
                "lat": "35.6895",
                "lon": "139.6917"
            ],
            "language": "japanese"
        ]
    ],
    "airports": [
        "germany": ["FRA", "MUC", "HAM", "TXL"]
    ]
]

I can access all fields with if let .. blocks optionally casting to something that I can work with, when reading.

However, I am currently writing unit tests where I need to selectively break dictionaries in multiple ways.

But I don't know how to elegantly remove keys from the dictionary.

For example I want to remove the key "japan" in one test, in the next "lat" should be nil.

Here's my current implementation for removing "lat":

if var countries = dict["countries"] as? [String: Any],
    var japan = countries["japan"] as? [String: Any],
    var capital = japan["capital"] as? [String: Any]
    {
        capital.removeValue(forKey: "lat")
        japan["capital"] = capital
        countries["japan"] = japan
        dictWithoutLat["countries"] = countries
}

Surely there must be a more elegant way?

Ideally I'd write a test helper that takes a KVC string and has a signature like:

func dictWithoutKeyPath(_ path: String) -> [String: Any] 

In the "lat" case I'd call it with dictWithoutKeyPath("countries.japan.capital.lat").

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

When working with a subscript, if the subscript is get/set and the variable is mutable, then the entire expression is mutable. However, due to the type cast the expression "loses" the mutability. (It's not an l-value anymore).

The shortest way to solve this is by creating a subscript that is get/set and does the conversion for you.

extension Dictionary {
    subscript(jsonDict key: Key) -> [String:Any]? {
        get {
            return self[key] as? [String:Any]
        }
        set {
            self[key] = newValue as? Value
        }
    }
}

Now you can write the following:

dict[jsonDict: "countries"]?[jsonDict: "japan"]?[jsonDict: "capital"]?["name"] = "berlin"

We liked this question so much that we decided to make a (public) Swift Talk episode about it: mutating untyped dictionaries


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...