【问题标题】:Clean way to filter an array of dictionaries by unique dictionary values通过唯一字典值过滤字典数组的干净方法
【发布时间】:2017-07-21 09:19:46
【问题描述】:

假设我有一个对象:

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]

假设有几百到几千个对象需要迭代。

【问题讨论】:

  • 如何使用这样的过滤器:fooArray.filter {($0.bar.contains(searchBar))}
  • 你的问题的标题和正文似乎不同——你有字典数组还是结构数组?
  • 我有一个结构数组,尽管这个问题的答案并不特别适用于结构类型。如果您感到困惑,请随意将标题编辑为更符合语义的内容,我对提出更好的标题没有信心。

标签: arrays swift dictionary filter


【解决方案1】:

一种可能的方法是使用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)

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

【讨论】:

  • 或者你可以直接说fooArray.filter { seenBarValues.insert($0.bar).inserted } :)
  • 这些都是很好的解决方案。谢谢
【解决方案2】:

我不喜欢接受的答案。将集合放在使用它的闭包之外似乎不合适。我宁愿把一切都包含在内。有一个 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-27
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 2019-03-31
    • 2019-10-04
    相关资源
    最近更新 更多