Tengo varios criterios para filtrar la matriz. Estos criterios son opcionales y se almacenan en struct, porque el usuario puede seleccionar solo una parte de ellos. Tengo variedad de modelos. Traté de usar el método de filtro, pero debe proporcionarle criterios no opcionales. ¿Qué enfoque debería ser para deshacerse de los opcionales y agregar ese criterio al método de filtro?
Filtrar estructura con opciones de filtrado
struct Filter { var brand: String? var price: Int? var consuption: Int? }clase de modelo
class CarOffer { var brand: String var price: Int var color: String var consumption: Int }Y aquí lo que traté de hacer, pero no tuve suerte porque filter.price es opcional y no sé si lo será o no. Entiendo que tengo que eliminar un opcional, pero ¿cómo agregar un criterio de filtro en el método de filtro según su opcionalidad? ¿O he elegido un enfoque equivocado?
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 }Le resultaría más fácil simplificar y dividir su código en partes más pequeñas. No hay ninguna razón por la cual una función para filtrar una matriz por algunas condiciones, también tenga que ser responsable de averiguar si un elemento cumple con esas condiciones. Te has atrapado mentalmente pensando que el predicado del filtro tiene que ser una larga cadena de condiciones && en un cierre.
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)Simplemente puede usar un valor predeterminado en lugar de filter de valores opcionales. Si usa el valor predeterminado de la offer , el filter simplemente devolverá verdadero en caso de que las propiedades opcionales sean nulas.
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 }Puede convertir los filtros en cierres y agregar un inicializador que permita pasar fácilmente los filtros que no nos interesan:
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 } }Esto brinda la mejor flexibilidad, ya que a partir de este momento puede agregar cualquier tipo de filtrado que desee. Como agregar un archivador basado en su estructura original, campos opcionales para ignorar:
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 } }