Подтвердить что ты не робот

Уменьшить массив для установки в Swift

Я пытаюсь уменьшить массив объектов до набора в Swift, и это мой код:

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

Однако я получаю сообщение об ошибке:

Type of expression is ambiguous without more context.

Я не понимаю, в чем проблема, поскольку тип URL определен как String. Любые идеи?

4b9b3361

Ответ 1

Вам не нужно уменьшать массив, чтобы получить его в набор; просто создайте набор с массивом: let objectSet = Set(objects.map { $0.URL }).

Ответ 2

reduce() метод ожидает закрытие, которое возвращает комбинированное значение, тогда как insert() методы значения Set не возвращают ничего, но вместо этого он вставляет новый элемент в существующий набор.

Чтобы заставить его работать, вам нужно сделать что-то вроде:

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

Но выше это немного ненужное осложнение. Если у вас большой массив, это означало бы создание множества постоянно растущих наборов, в то время как Swift перебирает все элементы из objects. Лучше следуйте советам @NRitH и используйте map(), поскольку это сделает результирующий набор за один раз.

Ответ 3

С Swift 5.1 вы можете использовать один из трех следующих примеров, чтобы решить вашу проблему.


# 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"

# 2. Использование 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"

# 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"

Ответ 4

Если URL для вашего объекта является строго типизированным String, вы можете создать новый объект Set<String> и использовать unionInPlace в наборе с отображенным массивом:

var mySet = Set<String>()
mySet.unionInPlace(objects.map { $0.URL as String })

Ответ 5

Если вы хотите напрямую сопоставить 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.