Зменшити масив для встановлення в Swift


80

Я намагаюся зменшити масив об'єктів до набору в Swift, і це мій код:

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

Однак я отримую повідомлення про помилку:

Type of expression is ambiguous without more context.

Я не розумію, в чому проблема, оскільки тип URL-адреси, безумовно, є String. Будь-які ідеї?


Я думаю, що підпис для зменшення - func reduce<T>(_ initial: T, @noescape combine combine: (T, Self.Generator.Element) throws -> T) rethrows -> Tце не те, що ви передаєте.
Мартін Маркончіні

Відповіді:


138

Вам не потрібно зменшувати масив, щоб потрапити в набір; просто створити набір з масивом: let objectSet = Set(objects.map { $0.URL }).


Але я хочу не набір об’єктів, а набір різних URL-адрес, які мають ці об’єкти. Я думаю, я міг би скласти карту, а потім встановити (карту), але я мав би змогу досягти цього за допомогою зменшення.
Банан

1
Ага. Тоді let objectSet = Set(objects.map { $0.URL }).
NRitH

Ах, добре, чудово. Не могли б ви відредагувати свою відповідь, щоб я міг її прийняти?
Банан

Це залежить. Якщо ви наносите на карту / плоску карту, це приємно об’єднати дзвінки.
pronebird

17

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


№1. Використовуючи Array«и map(_:)метод і Set» Sinit(_:) ініціалізатор

У найпростішому випадку ви можете зіставити початковий масив із масивом 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. За допомогою методу 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параметр закриття і повертає a 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 перебирає всі елементи з objects. Краще дотримуйтесь порад @NRitH і використовуйте, map()оскільки це зробить результуючий набір за один раз.


Дякую за цю відповідь, вона для мене з’ясовує проблему.
Банан

@Banana, np. Дивіться мою відредаговану відповідь. Це виявляється не лише надмірно ускладнюючим, але й неефективним завданням.
courteouselk

Це справді погане рішення. Кожного разу, коли ви створюєте новий набір екземплярів. developer.apple.com/documentation/swift/set/3128858-union . Union повертає новий набір.
В’ячеслав

1

Якщо URLна вашому об'єкті є строко набраний текст String, ви можете створити новий Set<String>об'єкт і використовувати unionInPlaceна наборі з відображеним масивом:

var mySet = Set<String>()
mySet.unionInPlace(objects.map { $0.URL as String })
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.