Aaron
Aaron

Reputation: 6714

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

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 = Foo(id: 1, bar: 1)
let foo2 = Foo(id: 2, bar: 1)
let foo3 = Foo(id: 3, bar: 2)
let foo4 = Foo(id: 4, bar: 3)
let foo5 = Foo(id: 5, bar: 3)

let fooArray = [foo1, foo2, foo3, foo4, foo5] 

What would be a clean way of filtering foo objects that have unique bar values?

// Desired output
let filteredArray = [foo1, foo3, foo4]

Assume there's anywhere from a few hundred to a few thousand objects to iterate upon.

Upvotes: 3

Views: 2740

Answers (2)

Daniel T.
Daniel T.

Reputation: 33979

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

Upvotes: 0

Martin R
Martin R

Reputation: 540075

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.

Upvotes: 5

Related Questions