web-dev-qa-db-fra.com

Réduire tableau pour définir Swift

J'essaie de réduire un tableau d'objets à un ensemble dans Swift et voici mon code:

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

Cependant, j'obtiens une erreur:

Type of expression is ambiguous without more context.

Je ne comprends pas quel est le problème, car le type d'URL est certainement String. Des idées?

59
Banana

Il n'est pas nécessaire de réduire un tableau pour l'intégrer à un ensemble. il suffit de créer l'ensemble avec un tableau: let objectSet = Set(objects.map { $0.URL }).

99
NRitH

Avec Swift 5.1, vous pouvez utiliser l'un des trois exemples suivants pour résoudre votre problème.


#1. Utilisation de Array ' map(_:) méthode et Set' init(_:) initialiseur

Dans le cas le plus simple, vous pouvez mapper votre tableau initial sur un tableau de urls (String), puis créer un ensemble à partir de ce tableau. Le code de Playground ci-dessous montre comment procéder:

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. Utilisation de la méthode reduce(into:_:) de Array

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"

Alternativement, vous pouvez utiliser la méthode Array de la méthode 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. Utiliser une extension Array

Si nécessaire, vous pouvez créer une méthode mapToSet pour Array qui prend un paramètre de fermeture transform et renvoie un Set. Le code de Playground ci-dessous montre comment l'utiliser:

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"
6
Imanou Petit

La méthode reduce() attend une fermeture qui retourne une valeur combinée, tandis que la méthode insert() de Set value ne renvoie rien, mais insère plutôt un nouvel élément dans l'ensemble existant.

Pour que cela fonctionne, vous devez faire quelque chose comme:

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

Mais ce qui précède est un peu une complication inutile. Si vous avez un grand tableau, cela signifierait un grand nombre d'ensembles en croissance constante tandis que Swift passe en revue tous les éléments de objects. Suivez mieux les conseils de @NRitH et utilisez map() comme cela produirait un ensemble résultant en une fois.

5
Anton Bronnikov

Si URL sur votre objet est un String fortement typé, vous pouvez créer un nouveau Set<String> object et utilise unionInPlace sur l’ensemble avec le tableau mappé:

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