import Foundation /// Eine Zeile einer flach ausgelegten Baumliste, mit ihrer Tiefe. /// /// Lagerorte und Kategorien sind Baeume, kommen vom Server aber als flache /// Liste nach Namen sortiert. Daraus wird eine Auswahl, in der "Garage → Fach A" /// und "Keller → Fach A" irgendwo zwischen allem anderen stehen - Geschwister /// und Kinder finden nie zusammen. Diese Reihenfolge stellt das her. struct Baumzeile: Identifiable { let item: T let tiefe: Int var id: T.ID { item.id } /// Name mit Einrueckung je Ebene. /// /// Eingerueckt wird mit fuehrenden Leerzeichen, weil ein aufklappendes /// Auswahlmenue ausser Text nichts annimmt - Layout ignoriert es. /// Bekannte Grenze: Bricht ein langer Name auf eine zweite Zeile um, /// faengt die wieder ganz links an. Eine Listendarstellung /// (`.pickerStyle(.navigationLink)`) koennte das, riss aber im Blatt den /// ganzen Navigationsstapel mit und ist deshalb wieder draussen. var label: String { String(repeating: " ", count: tiefe) + item.name } } /// Bringt eine flache Eltern-Kind-Liste in Baumreihenfolge, mit Tiefe. /// /// Waisen - deren Elternteil weggefiltert oder geloescht ist - gelten als /// oberste Ebene. Ohne diese Regel verschwaenden sie lautlos aus der Auswahl. func baum(_ liste: [T]) -> [Baumzeile] { let ids = Set(liste.map(\.id)) var ergebnis: [Baumzeile] = [] func gehe(_ knoten: T, _ tiefe: Int) { ergebnis.append(Baumzeile(item: knoten, tiefe: tiefe)) for kind in liste.filter({ $0.parentId == knoten.id }) { gehe(kind, tiefe + 1) } } for wurzel in liste.filter({ $0.parentId == nil || !ids.contains($0.parentId!) }) { gehe(wurzel, 0) } return ergebnis }