From db41e68f35fb66fec9a05ce693b629e585ca51b5 Mon Sep 17 00:00:00 2001 From: relikd Date: Fri, 5 Jun 2020 16:11:42 +0200 Subject: [PATCH] Remove filter logic from PipelineSorting --- main/Common Classes/FilterPipeline.swift | 79 +++++++++++------------- 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/main/Common Classes/FilterPipeline.swift b/main/Common Classes/FilterPipeline.swift index 15ff5c0..7c916b1 100644 --- a/main/Common Classes/FilterPipeline.swift +++ b/main/Common Classes/FilterPipeline.swift @@ -71,8 +71,8 @@ class FilterPipeline { } } - /// Returns the index set of either the last filter layer, or `dataSource` if no filter is set yet. - fileprivate func lastFilterLayerIndices() -> [Int] { + /// Returns the index set of either the last filter layer, or `dataSource` if no filter is set. + fileprivate func lastLayerIndices() -> [Int] { pipeline.last?.selection ?? dataSource.indices.arr() } @@ -96,32 +96,30 @@ class FilterPipeline { pipeline.insert(newFilter, at: i) resetFilters(startingAt: i) } else { - newFilter.reset(to: dataSource, previous: pipeline.last) + newFilter.reset(to: dataSource, previous: lastLayerIndices()) pipeline.append(newFilter) - display?.apply(moreRestrictive: newFilter) + display?.apply(moreRestrictive: newFilter.selection) } if cellAnimations { delegate?.tableView.reloadData() } } /// Find and remove filter with given identifier. Will automatically update remaining filters and display sorting. func removeFilter(withId ident: String) { - if let i = indexOfFilter(ident) { - pipeline.remove(at: i) - if i == pipeline.count { - // only if we don't reset other layers we can assure `toLessRestrictive` - display?.reset(toLessRestrictive: pipeline.last) - } else { - resetFilters(startingAt: i) - } + guard let i = indexOfFilter(ident) else { return } + pipeline.remove(at: i) + if i == pipeline.count { + // only if we don't reset other layers we can assure `toLessRestrictive` + display?.reset(toLessRestrictive: lastLayerIndices()) + } else { + resetFilters(startingAt: i) } if cellAnimations { delegate?.tableView.reloadData() } } /// Start filter evaluation on all entries from previous filter. func reloadFilter(withId ident: String) { - if let i = indexOfFilter(ident) { - resetFilters(startingAt: i) - } + guard let i = indexOfFilter(ident) else { return } + resetFilters(startingAt: i) if cellAnimations { delegate?.tableView.reloadData() } } @@ -129,7 +127,7 @@ class FilterPipeline { // func popLastFilter(k: Int = 1) { // guard k > 0, k <= pipeline.count else { return } // pipeline.removeLast(k) -// display?.reset(toLessRestrictive: pipeline.last) +// display?.reset(toLessRestrictive: lastFilterLayerIndices()) // if cellAnimations { delegate?.tableView.reloadData() } // } @@ -144,12 +142,13 @@ class FilterPipeline { /// - Parameter index: Must be: `index <= pipeline.count` private func resetFilters(startingAt index: Int = 0) { for i in index..0) ? pipeline[i-1] : nil) + pipeline[i].reset(to: dataSource, previous: (i>0) + ? pipeline[i-1].selection : dataSource.indices.arr()) } // Reset is NOT less-restrictive because filters are dynamic // Calling reset on a filter twice may yield different results // E.g. if filter uses variables outside of scope (current time, search term) - display?.reset() + display?.reset(to: lastLayerIndices()) } /// Push object through filter pipeline to check whether it survives all filters. @@ -269,10 +268,9 @@ class PipelineFilter { shouldPersist = predicate } - /// Reset selection indices by copying the indices from the previous filter or using - /// the indices of the data source if no previous filter is present. - fileprivate func reset(to dataSource: [T], previous filter: PipelineFilter? = nil) { - selection = (filter != nil) ? filter!.selection : dataSource.indices.arr() + /// Reset `selection` by copying the indices and applying the filter function + fileprivate func reset(to dataSource: [T], previous filterIndices: [Int]) { + selection = filterIndices selection.removeAll { !shouldPersist(dataSource[$0]) } } @@ -356,7 +354,6 @@ class PipelineSorting { private(set) var projection: [Int] = [] private let comperator: (Int, Int) -> Bool // links to pipeline.dataSource - private let previousLayerIndices: () -> [Int] // links to pipeline /// Create a fresh, already sorted, display order projection. /// - Parameter predicate: Return `true` if first element should be sorted before second element. @@ -364,34 +361,28 @@ class PipelineSorting { comperator = { [unowned pipe] in predicate(pipe.dataSource[$0], pipe.dataSource[$1]) } - previousLayerIndices = { [unowned pipe] in - pipe.lastFilterLayerIndices() - } - reset() + reset(to: pipe.lastLayerIndices()) } - /// Apply a new layer of filtering. Every layer can only restrict the display even further. + /// Replace current `projection` with new filter indices and apply sorting. + /// - Complexity: O(*n* log *n*), where *n* is the length of the `filter`. + fileprivate func reset(to filterIndices: [Int]) { + projection = filterIndices.sorted(by: comperator) + } + + /// After adding a new layer of filtering the new layer can only restrict the display even further. /// Therefore, indices that were removed in the last layer will be removed from the projection too. /// - Complexity: O(*m* log *n*), where *m* is the length of the `projection` and *n* the length of the `filter`. - fileprivate func apply(moreRestrictive filter: PipelineFilter) { - projection.removeAll { filter.index(ofDataSource: $0) == nil } + fileprivate func apply(moreRestrictive filterIndices: [Int]) { + projection.removeAll { !filterIndices.binTreeExists($0, compare: (<)) } } - /// Remove a layer of filtering. Previous layers are less restrictive and contain more indices. + /// After removing a layer of filtering the previous layers are less restrictive and thus contain more indices. /// Therefore, the difference between both index sets will be inserted into the projection. - /// - Parameter filter: If `nil`, reset to last filter layer or `dataSource` - /// - Complexity: - /// * O(*m* log *n*), if `filter != nil`. - /// Where *n* is the length of the `projection` and *m* is the difference between both layers. - /// * O(*n* log *n*), if `filter == nil`. - /// Where *n* is the length of the previous layer (or `dataSource`). - fileprivate func reset(toLessRestrictive filter: PipelineFilter? = nil) { - if let indices = filter?.selection.difference(toSubset: projection.sorted(), compare: (<)) { - for idx in indices { - insertNew(idx) - } - } else { - projection = previousLayerIndices().sorted(by: comperator) + /// - Complexity: O(*m* log *n*), where *m* is the difference to the previous layer and *n* is the length of the `projection`. + fileprivate func reset(toLessRestrictive filterIndices: [Int]) { + for x in filterIndices.difference(toSubset: projection.sorted(), compare: (<)) { + insertNew(x) } }