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?

Banane
quelle
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 }).

NRitH
quelle
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"
Imanou Petit
quelle
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.

Höflichkeit
quelle
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 })
JAL
quelle