简体   繁体   中英

Swift filter method with dictionaries

I have an initial array of filter values, for example :

let filterValues : [String] = ["1", "4"]

I try to filter an array of dictionaries, where each dictionary contains an array of 1 dictionary or more.

The form of the array is the following :

  let test : [[String : AnyObject]] = [
        ["Objects":
            [
                [
                    "value":"1",
                    "test":"test"
                ],
                [
                    "value":"1",
                    "test":"test"
                ],
                [
                    "value":"1",
                    "test":"test"
                ],
                [
                    "value":"4",
                    "test":"test"
                ],
                [
                    "value":"2",
                    "test":"test"
                ],
                [
                    "value":"2",
                    "test":"test"
                ]
            ]
        ],
        ["Objects":
            [
                [
                    "value":"4",
                    "test":"test"
                ],
                [
                    "value":"2",
                    "test":"test"
                ],
                [
                    "value":"2",
                    "test":"test"
                ]
            ]
        ]
    ]

What I want after applying the filter method on my initial test array :

    [
            ["Objects":
                [
                    [
                        "value":"1",
                        "test":"test"
                    ],
                    [
                        "value":"1",
                        "test":"test"
                    ],
                    [
                        "value":"1",
                        "test":"test"
                    ],
                    [
                        "value":"4",
                        "test":"test"
                    ]
                ]
            ],
            ["Objects":
                [
                    [
                        "value":"4",
                        "test":"test"
                    ]
                ]
            ]
        ]

My code in playground :

//Filter objects with matching enabled filters
let filtered = test.filter({
    let currentObjects = $0["Objects"] as! [[String:AnyObject]]
    let currentsObjectsFiltered = currentObjects.filter({
        if let value = $0["value"] as? String {
            return filterValues.contains(value)
        }
        return false
    })
    print(currentsObjectsFiltered)
    return false
})

The array " currentsObjectsFiltered " is well filtered with the required values. But I want the original " filtered " array with the structure of the test array, not a new array which is simply filtered with the new objects. I don't know what to do with my filtered array " currentsObjectsFiltered "

I think that I must use it now that I have the objects sorted, but how...

EDIT

currentsObjectsFiltered output :

[["value": 1, "test": test], ["value": 1, "test": test], ["value": 1, "test": test], ["value": 1, "test": test], ["value": 4, "test": test], ["value": 4, "test": test]]
[["value": 1, "test": test], ["value": 1, "test": test], ["value": 1, "test": test], ["value": 1, "test": test], ["value": 4, "test": test], ["value": 4, "test": test]]

Don't pay attention to number of results values in the output I don't have the same test array, I just post the output to show you the structure of currentsObjectsFiltered which is well filtered.

I think I can't do it in one filter step, because the filter method is applied only to the children of the filtered array, not the children of the children. So I can just filter the " Objects " according a statement, I can't filter the children of " Objects " dictionaries, anybody can confirm that ?

Any help ? Thanks.

This is possible, but the types are very annoying to work with. If you're finding yourself creating new AnyObject types, then you're almost certainly approaching the problem wrong. AnyObject mostly exists so you can deal with id types that come back from Cocoa. It's not a general-purpose type for Swift.

If you first would convert these to a type-safe struct, then all of this is much simpler and you won't have to constantly up-and-downcast everything into AnyObject .

Here's a simple struct that can be constructed from one of your dictionaries:

struct Object {
    let value: Int
    let test: String

    init?(jsonDictionary: [String: String]) {
        guard let
            value = jsonDictionary["value"].flatMap({Int($0)}),
            test = jsonDictionary["test"]
            else { return nil }

        self.value = value
        self.test = test
    }
}

Converting your whole data structure into this is straightforward:

let testArray = test.flatMap {
    ($0["Objects"] as? [[String: String]])?
        .flatMap(Object.init)
}

Then filtering it as you desire is much easier:

let filterValues = [1, 4]

let filtered = testArray.map { objects in
    return objects.filter { filterValues.contains($0.value) }
}

Even if you need the final result to involve AnyObject , I'd still do it this way, and then write a function to serialize it back to your original AnyObject structure. Working directly with AnyObject is just too error-prone and complicated. As an example of converting back to the AnyObject form, you could use a function like this:

func jsonArrayFromObjects(objectList: [[Object]]) -> [[String: AnyObject]] {
    return objectList.map { objects in
        ["Objects": objects.map { ["value": "\($0.value)", "test": $0.test] }]
    }
}

But I'd avoid this unless it were absolutely necessary. Even if you need a dictionary, it would be better to make this [[String: [[String: String]]] rather than [[String: AnyObject]] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM