J'ai trois objets imbriqués via des listes comme ceci:
class Canteen: Object {
dynamic var name: String?
let lines = List<Line>()
}
class Line: Object {
dynamic var name: String?
let meals = List<Meal>()
}
class Meal: Object {
dynamic var name: String?
dynamic var vegan: Bool = false
}
Obtenir toutes les cantines avec toutes les lignes et les repas n'est pas un problème. Voici ce que je fais en ce moment:
let predicate = NSPredicate(format: "name == %@", selectedCanteenType.rawValue)
canteens = realm.objects(Canteen).filter(predicate)
Mais maintenant, je n'ai besoin que des repas végétaliens. Je cherche donc à obtenir la cantine choisie avec toutes les lignes, mais uniquement avec des repas végétaliens. Est-ce possible dans le domaine de filtrer les listes dans les objets récupérés?
Realm n'a aucune sorte de concept de vue filtrée en profondeur, vous ne pouvez donc pas avoir un Results<Canteen>
qui limite le List
s contenu dans les objets liés aux repas végétaliens.
Vous pouvez faire plusieurs choses similaires. Vous pouvez ajouter des propriétés de relation inverse, puis interroger à la place les objets Meal
:
class Canteen: Object {
dynamic var name: String?
let lines = List<Line>()
}
class Line: Object {
dynamic var name: String?
let meals = List<Meal>()
let canteens = LinkingObjects(fromType: Canteen.self, property: "lines")
}
class Meal: Object {
dynamic var name: String?
dynamic var vegan: Bool = false
let lines = LinkingObjects(fromType: Line.self, property: "meals")
}
let meals = realm.objects(Meal).filter("vegan = true AND ANY lines.canteens.name = %@", selectedCanteenType.rawValue)
(Ou plutôt, vous pourrez quand le domaine 0.102.1 sera épuisé; cela se bloque actuellement).
Si vous avez juste besoin de parcourir les repas mais de le faire à la cantine, vous pouvez faire:
let canteens = realm.objects(Canteen).filter("name = %@ AND ANY lines.meals.vegan = true", selectedCanteenType.rawValue)
for canteen in canteens {
for line in canteen.lines.filter("ANY meals.vegan = true") {
for meal in line.meals.filter("vegan = true") {
// do something with your vegan meal
}
}
}
Malheureusement, il y a une certaine duplication due à la nécessité de répéter le filtre pour chaque niveau des références.
Essaye ça:
let predicate = NSPredicate(format: "name == %@", "")
var canteens: [Canteen] = realm.objects(Canteen).filter(predicate).map { can in
// Iterate through all the Canteens
let lines: [Line] = can.lines.map { (line: Line) in
// Iterate through all the lines in each canteene
// Filter all the Meals that are NOT vegan
let meals = line.meals.filter { $0.vegan == true }
line.meals = List<Meal>(meals)
return line
}
can.lines = List<Line>(lines)
return can
}
Le royaume lui permet d'utiliser des fonctions en tant que paramètre pour le filtrage. C'est donc ma solution que j'utilise actuellement.
Les deux fonctions de filtre:
func vegetarianFilter(_ meal: Meal) -> Bool {
if showVegetarianOnly {
if(meal.veg || meal.vegan){
return true
}
return false
}
return true
}
func filterEmptyLines(_ line: Line) -> Bool {
if(line.meals.filter(vegetarianFilter).count > 0){
return true
}
return false
}
Les fonctions filtrent tous les repas qui ne sont ni végétariens ni végétaliens lorsque l'utilisateur a sélectionné showVegetarianOnly = true
. En outre, il filtre toutes les lignes qui n'ont plus de repas (rien n'est végétarien ou végétalien).
Fonctions les plus importantes de la TableView:
override func numberOfSections(in tableView: UITableView) -> Int {
return canteenDay?.lines.filter(filterEmptyLines).count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return canteenDay?.lines.filter(filterEmptyLines)[section].meals.filter(vegetarianFilter).count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let meal = canteenDay!.lines.filter(filterEmptyLines)[indexPath.section].meals.filter(vegetarianFilter)[indexPath.row]
cell.textLabel?.text = meal.meal
return cell
}