Search code examples
swiftdictionaryswift3nsdictionary

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!


Solution

  • 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
    }()