减少数组在Swift中设置


78

我正在尝试将对象数组简化为Swift中的集合,这是我的代码:

objects.reduce(Set<String>()) { $0.insert($1.URL) }

但是,我得到一个错误:

Type of expression is ambiguous without more context.

我不明白问题是什么,因为URL的类型肯定是String。有任何想法吗?


我认为reduce的签名func reduce<T>(_ initial: T, @noescape combine combine: (T, Self.Generator.Element) throws -> T) rethrows -> T不是您要传递的签名。
Martin Marconcini 2015年

Answers:


134

您不必简化数组就可以将其放入集合中。只需使用数组创建集合:let objectSet = Set(objects.map { $0.URL })


但是我不想要对象集,而是想要这些对象具有的不同URL集。我想我可以先做一个映射,然后再做Set(map),但是我应该可以通过reduce来实现。
香蕉2015年

1
啊。然后let objectSet = Set(objects.map { $0.URL })
NRitH 2015年

啊,好的,太好了。您可以编辑答案以便我接受吗?
香蕉2015年

这取决于。如果您映射/展开地图,则很适合链接调用。
pronebird

16

使用Swift 5.1,您可以使用以下三个示例之一来解决您的问题。


#1 使用Arraymap(_:)方法和Setinit(_:)初始值设定项

在最简单的情况下,您可以将初始数组映射到urls(String)的数组,然后从该数组创建一个集合。下面的Playground代码显示了操作方法:

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlArray = objectArray.map({ $0.url })
let urlSet = Set(urlArray)
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

#2。使用Arrayreduce(into:_:)方法

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlSet = objectArray.reduce(into: Set<String>(), { (urls, object) in
    urls.insert(object.url)
})
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

或者,您可以使用Arrayreduce(_:_:)方法:

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlSet = objectArray.reduce(Set<String>(), { (partialSet, object) in
    var urls = partialSet
    urls.insert(object.url)
    return urls
})
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

#3。使用Array扩展

如有必要,您可以mapToSet为该方法创建一个Array带有transform闭合参数并返回的方法Set。下面的Playground代码显示了如何使用它:

extension Array {

    func mapToSet<T: Hashable>(_ transform: (Element) -> T) -> Set<T> {
        var result = Set<T>()
        for item in self {
            result.insert(transform(item))
        }
        return result
    }

}

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlSet = objectArray.mapToSet({ $0.url })
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

5

reduce()方法期望闭包返回一个组合值,而值insert()方法Set不返回任何值,而是将一个新元素插入到现有集中。

为了使其正常工作,您需要执行以下操作:

objects.reduce(Set<String>()) {
    $0.union(CollectionOfOne($1.URL))
}

但是以上只是一些不必要的复杂性。如果数组很大,则意味着Swift将遍历from中的所有元素时,将创建大量不断增长的集合objects。最好遵循@NRitH的建议,并使用map()它,因为这样可以一次性完成结果集。


感谢您提供此答案,它可以为我解决问题。
香蕉2015年

@香蕉,np。看到我编辑的答案。事实证明,这样做不仅复杂,而且效率低下。
Courteouselk

这是一个非常糟糕的解决方案。每次您创建一个新的集合实例。developer.apple.com/documentation/swift/set/3128858-union。联合返回一个新集合。
维亚切斯拉夫

1

如果URL在您的对象上是一个强类型String,则可以创建一个新Set<String>对象,并unionInPlace在具有映射数组的集合上使用:

var mySet = Set<String>()
mySet.unionInPlace(objects.map { $0.URL as String })
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.