Clean way to filter an array of dictionaries by unique dictionary values

前端 未结 2 1539
情深已故
情深已故 2021-01-14 06:04

Say I have an object:

struct Foo {
    let id: Int
    let bar: Int
}

Now I have 5 of these objects in an array:

let foo1 =         


        
相关标签:
2条回答
  • 2021-01-14 06:39

    I'm not a fan of the accepted answer. Having the set outside the closure that uses it doesn't seem appropriate. I would rather keep everything contained. There's a CS term that applies but I don't remember what it is...

    I would rather see this:

    let uniquedBars = fooArray
        .reduce(into: (result: [Foo](), set: Set<Int>())) { partial, next in
            if partial.set.insert(next.bar).inserted {
                partial.result.append(next)
            }
        }
        .result
    
    0 讨论(0)
  • 2021-01-14 06:44

    One possible approach is to use a Set which keeps track of which bar values have already been seen:

    var seenBarValues = Set<Int>()
    let filteredArray = fooArray.filter { foo in
        if seenBarValues.contains(foo.bar) {
            // We already had a `Foo` with this `bar` value: skip.
            return false 
        } else {
            // First `Foo` with this `bar` value: remember and include.
            seenBarValues.insert(foo.bar)
            return true
        }
    }
    

    As @Hamish correctly pointed out, this can be shortened to

    var seenBarValues = Set<Int>()
    let filteredArray = fooArray.filter { 
        seenBarValues.insert($0.bar).inserted
    }
    

    using the fact that

    public mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element)
    

    returns a tuple whose first member indicates if an element equal to the newly inserted one was already present in the set.

    0 讨论(0)
提交回复
热议问题