swiftnsarraynsmutablearrayswift5nsorderedset

Reorder NSOrdered Set based on the order specified in a different NSOrderedSet


So, I have two ordered sets and things are structured in a way where one ordered set basically has a list of dictionaries and the other ordered set has strings that correspond with a certain value in the dictionar. Let me illustrate, using an example:

OrderedSetA = [["key1": "test", "key2": "A"], ["key1": "test2", "key2": "B"], ["key1": "test3", "key2":"C"], ["key1": "test4", "key2": "E"]]
OrderedSetB = ["B","G","H", "A", "E", "D", "C", "F", "J", "K"]

So, now I basically want to rearrange elements in OrderedSetA based on the order specified for strings that correspond with key2 in OrderedSet B while ignoring any of the values in B that aren't in A. So for example, in this scenario, what I'd be looking for is something like:

OrderedSetC = [["key1": "test2", "key2": "B"], ["key1": "test1", "key2": "A"], ["key1": "test4", "key2":"E"], ["key1": "test3", "key2": "C"]]

Here's what I've tried so far:

let orderedSetC = orderedSetA.filter {OrderedSetB.contains(($0 as AnyObject).key2)}

And what happens here is that it just returns the same thing, I think I'm filtering based on whatever is OrderedSetB, that isn't in OrderedSetA however there is no sorting/reordering happening.

What would be the most appropriate and effective Swift 5 based way of handling this?

Thanks!


Solution

  • You can use filter with a predicate:

    let osA: NSOrderedSet = ["A", "B", "C", "D", "E", "F"]
    let osB: NSOrderedSet = ["B", "G", "H", "A", "C", "D", "E", "F", "J", "K"]
    
    let osC = osB.filtered(using: .init(block: { (element, _) in
        osA.contains(element as Any)
    }))
    

    You can also create a NSMutableOrderedSet of your ordered set B and intersect with A:

    var osC = osB.mutableCopy() as! NSMutableOrderedSet
    osC.intersect(osA)
    print(Array(osC))   // ["B", "A", "C", "D", "E", "F"]
    

    Another option is to implement your own native Swift OrderedSet as I have posted in this answer. After adding the OrderedSet to your project you could simply get ordered set A intersection ordered set B:

    let osA: OrderedSet = ["A", "B", "C", "D", "E", "F"]
    let osB: OrderedSet = ["B", "G", "H", "A", "C", "D", "E", "F", "J", "K"]
    
    let osC = osB.intersection(osA)  // ["B", "A", "C", "D", "E", "F"] 
    


    edit/update:

    Not sure why would you need a NSOrderedSet of dictionaries. IMO you should structure your data. Anyway if you really want to do it this way you can use reduce(into:):

    let osA: NSOrderedSet = [["key1": "test", "key2": "A"], ["key1": "test2", "key2": "B"], ["key1": "test3", "key2":"C"], ["key1": "test4", "key2": "E"]]
    let osB: NSOrderedSet = ["B","G","H", "A", "E", "D", "C", "F", "J", "K"]
    
    let osC: NSMutableOrderedSet = osB.reduce(into: .init(), { (os, any) in
        guard let object = osA.first(where: { element in
            (element as? [String: String] ?? [:])?.values.contains(any as? String ?? "") == true
        }) else { return }
        os.add(object)
    })