通过独特的词典值过滤词典的清洁方法



说我有一个对象:

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

现在,我在一个数组中有5个对象:

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] 

过滤具有唯一bar值的foo对象的干净方法是什么?

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

假设有几百到几千个物体在任何地方进行迭代。

一种可能的方法是使用Set,该方法跟踪哪个方法 bar值已经看到:

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
    }
}

正如@hamish正确指出的那样,可以将其缩短为

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

使用

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

返回一个元组,其第一个成员指示元素是否等于在集合中已经存在于新插入的。

我不喜欢被接受的答案。在使用它的闭合外,将设置看起来不合适。我宁愿保留所有内容。有一个CS术语适用,但我不记得它是什么...

我宁愿看到这一点:

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

最新更新