Reduzieren Sie das Array auf Swift


78

Ich versuche, ein Array von Objekten auf eine Menge in Swift zu reduzieren, und dies ist mein Code:

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

Ich erhalte jedoch eine Fehlermeldung:

Type of expression is ambiguous without more context.

Ich verstehe nicht, was das Problem ist, da der Typ der URL definitiv String ist. Irgendwelche Ideen?


Ich denke, die Signatur für Reduzieren ist func reduce<T>(_ initial: T, @noescape combine combine: (T, Self.Generator.Element) throws -> T) rethrows -> T, was Sie nicht übergeben.
Martin Marconcini

Antworten:


134

Sie müssen ein Array nicht verkleinern, um es in einen Satz zu integrieren. Erstellen Sie einfach das Set mit einem Array : let objectSet = Set(objects.map { $0.URL }).


Aber ich möchte nicht die Menge der Objekte, sondern die Menge der verschiedenen URLs, die diese Objekte haben. Ich denke, ich könnte eine Karte machen und dann Set (Karte), aber ich sollte dies mit Reduzieren erreichen können.
Banane

1
Ah. Dann let objectSet = Set(objects.map { $0.URL }).
NRitH

Ah, okay, großartig. Könnten Sie vielleicht Ihre Antwort bearbeiten, damit ich sie akzeptieren kann?
Banane

Es hängt davon ab, ob. Wenn Sie Map / Flatmap sind, ist es schön, Anrufe zu verketten.
Pronebird

16

Mit Swift 5.1 können Sie eines der drei folgenden Beispiele verwenden, um Ihr Problem zu lösen.


# 1. Verwenden Arrayder map(_:)Methode und Setdes init(_:)Initialisierers

Im einfachsten Fall können Sie Ihr ursprüngliches Array einem Array von urls ( String) zuordnen und dann aus diesem Array einen Satz erstellen. Der folgende Code für den Spielplatz zeigt, wie es geht:

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. Mit Arrayder reduce(into:_:)Methode von

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"

Als Alternative, können Sie Array‚s - reduce(_:_:)Methode:

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. Verwenden einer ArrayErweiterung

Bei Bedarf können Sie eine mapToSetMethode erstellen Array, die einen transformAbschlussparameter verwendet und a zurückgibt Set. Der folgende Code für den Spielplatz zeigt, wie er verwendet wird:

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()Die Methode erwartet einen Abschluss, der einen kombinierten Wert zurückgibt , während die insert()Wertmethoden Setnichts zurückgeben, sondern stattdessen ein neues Element in die vorhandene Menge einfügen.

Damit es funktioniert, müssten Sie Folgendes tun:

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

Aber das Obige ist eine unnötige Komplikation. Wenn Sie ein großes Array haben, bedeutet dies, dass eine ganze Reihe von ständig wachsenden Sets erstellt werden müssen, während Swift alle Elemente von durchläuft objects. Befolgen Sie besser die Ratschläge von @NRitH und verwenden Sie diese, map()da dies einen resultierenden Satz auf einmal ergeben würde.


Vielen Dank für diese Antwort, sie klärt das Problem für mich.
Banane

@ Banana, np. Siehe meine bearbeitete Antwort. Es stellt sich heraus, dass dies nicht nur zu kompliziert, sondern auch ineffizient ist.
Courteouselk

Dies ist eine wirklich schlechte Lösung. Jedes Mal, wenn Sie einen neuen Set-Instanse erstellen. developer.apple.com/documentation/swift/set/3128858-union . Union Gibt einen neuen Satz zurück.
Vyacheslav

1

Wenn es sich URLbei Ihrem Objekt um ein stark typisiertes Objekt handelt String, können Sie ein neues Set<String>Objekt erstellen und unionInPlaceam Set mit dem zugeordneten Array verwenden:

var mySet = Set<String>()
mySet.unionInPlace(objects.map { $0.URL as String })
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.