Ответ 1
Вам не нужно уменьшать массив, чтобы получить его в набор; просто создайте набор с массивом: let objectSet = Set(objects.map { $0.URL })
.
Я пытаюсь уменьшить массив объектов до набора в Swift, и это мой код:
objects.reduce(Set<String>()) { $0.insert($1.URL) }
Однако я получаю сообщение об ошибке:
Type of expression is ambiguous without more context.
Я не понимаю, в чем проблема, поскольку тип URL определен как String. Любые идеи?
Вам не нужно уменьшать массив, чтобы получить его в набор; просто создайте набор с массивом: let objectSet = Set(objects.map { $0.URL })
.
reduce()
метод ожидает закрытие, которое возвращает комбинированное значение, тогда как insert()
методы значения Set
не возвращают ничего, но вместо этого он вставляет новый элемент в существующий набор.
Чтобы заставить его работать, вам нужно сделать что-то вроде:
objects.reduce(Set<String>()) {
$0.union(CollectionOfOne($1.URL))
}
Но выше это немного ненужное осложнение. Если у вас большой массив, это означало бы создание множества постоянно растущих наборов, в то время как Swift перебирает все элементы из objects
. Лучше следуйте советам @NRitH и используйте map()
, поскольку это сделает результирующий набор за один раз.
С Swift 5.1 вы можете использовать один из трех следующих примеров, чтобы решить вашу проблему.
Array
map(_:)
и Set
init(_:)
initializer В простейшем случае вы можете сопоставить исходный массив с массивом url
(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"
Array
reduce(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"
В качестве альтернативы вы можете использовать метод Array
reduce(_:_:)
:
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"
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"
Если URL
для вашего объекта является строго типизированным String
, вы можете создать новый объект Set<String>
и использовать unionInPlace
в наборе с отображенным массивом:
var mySet = Set<String>()
mySet.unionInPlace(objects.map { $0.URL as String })
Если вы хотите напрямую сопоставить Array
с Set
путем вызова метода, такого как reduce
на нем, вы можете создать расширение на Array
, которое преобразует его как целое, а не преобразует элементы в он похож на обычный Swift map
.
extension Array {
func mapArray<T>(_ transform: (Array) -> T) -> T {
return transform(self)
}
}
Затем вы можете передать в инициализатор Set новую функцию mapArray
objectArray
.map({ $0.URL })
.mapArray(Set.init)
Просто помните, что для инициализации Set
, как это, Array
, возвращенный с первого метода map
, должен иметь его элементы, соответствующие протоколу Hashable
.