2
votes

Comment filtrer un tableau dans Swift avec plusieurs critères optionnels?

J'ai plusieurs critères avec lesquels filtrer le tableau. Ces critères sont optionnels et stockés dans struct, car l'utilisateur ne peut en sélectionner qu'une partie. J'ai une panoplie de modèles. J'ai essayé d'utiliser la méthode de filtrage, mais vous devez lui fournir des critères non facultatifs. Quelle approche devrait être pour se débarrasser des options et ajouter ces critères à la méthode de filtrage?

Structure de filtrage avec options de filtrage

let offers: [CarOffer] = […]

func applyFilter(filter: Filter) -> [CarOffer] {

let filteredOffers = offers.filter { $0.brand == filter.brand && $0.price <= filter.price && $0.consumption <= filter.consumption }

return filteredOffers
}

Classe de modèle

class CarOffer {
  var brand: String
  var price: Int
  var color: String
  var consumption: Int
}

Et voici ce que j'ai essayé de faire, mais pas de chance car filter.price est optionnel et je ne sais pas si ce sera le cas ou non. Je comprends que je dois supprimer une option, mais comment ajouter un critère de filtre dans la méthode de filtre en fonction de son option? Ou j'ai choisi une mauvaise approche?

struct Filter {
  var brand: String?
  var price: Int?
  var consuption: Int?
}


0 commentaires

3 Réponses :


1
votes

Vous pouvez simplement utiliser une valeur par défaut au lieu des valeurs facultatives de filter . Si vous utilisez la valeur par défaut de l ' offre , filter retournera simplement vrai au cas où les propriétés facultatives seraient nulles.

func applyFilter(filter: Filter) -> [CarOffer] {

    let filteredOffers = offers.filter { $0.brand == filter.brand && $0.price <= (filter.price ?? $0.price) && $0.consumption <= (filter.consumption ?? $0.consumption) }

    return filteredOffers
}


3 commentaires

Je pense que vous devez également fournir une valeur par défaut pour la marque, mais en montre suffisamment pour répondre à la question


@Scriptable non, car == est utilisé pour brand , qui fonctionne également pour les options


J'ai simplement modifié la réponse $ 0.brand == (filter.brand? .String ?? $ 0.brand) - en vérifiant que la chaîne est vide ou non (var string: String? {Return self.isEmpty? Nil: self}. Fonctionne comme je veux



4
votes

Vous auriez plus de facilité en simplifiant et en divisant votre code en plus petits morceaux. Il n'y a aucune raison pour qu'une fonction de filtrage d'un tableau selon certaines conditions doive également être responsable de déterminer si un élément remplit ces conditions. Vous vous êtes pris au piège mentalement en pensant que le prédicat de filtre est une longue chaîne de conditions && dans une fermeture.

struct CarOffer {
    let brand: String
    let price: Int
    let color: String
    let consumption: Int
}

struct CarFilter {
    let brand: String?
    let price: Int?
    let consumption: Int?

    func matches(car: CarOffer) -> Bool {
        if let brand = self.brand, brand != car.brand { return false }
        if let price = self.price, price != car.price { return false }
        if let consumption = self.consumption, consumption != car.consumption { return false }
        return true
    }
}

extension Sequence where Element == CarOffer {
    func filter(carFilter: CarFilter) -> [CarOffer] {
        return self.filter(carFilter.matches)
    }
}

let filter = CarFilter(brand: nil, price: nil, consumption: nil)
let offers: [CarOffer] = [] //...
let filteredOffers = offers.filter(carFilter: filter)


1 commentaires

@LeoDabus True.



1
votes

Vous pouvez convertir les filtres en fermetures, et ajouter un initialiseur qui permet de passer facilement des filtres dont nous ne nous soucions pas:

init(brand: String? = nil,
     price: Int?  = nil,
     consuption: Int? = nil) {
    self.brand = { brand == nil || brand == $0 }
    self.price = { price == nil || price! <= $0 }
    self.consuption = { consuption == nil || consuption! <= $0 }
}

Cela donne la meilleure flexibilité, à partir de maintenant vous pouvez ajouter tout type de filtrage que vous souhaitez. Comme l'ajout d'un fichier basé sur votre structure d'origine, des options pour les champs à ignorer:

struct Filter {
    var brand: (String) -> Bool
    var price: (Int) -> Bool
    var consuption: (Int) -> Bool

    init(brand: @escaping (String) -> Bool = { _ in return true },
         price: @escaping (Int) -> Bool = { _ in  return true },
         consuption: @escaping (Int) -> Bool = { _ in  return true }) {
        self.brand = brand
        self.price = price
        self.consuption = consuption
    }
}


0 commentaires