Skip to content

Instantly share code, notes, and snippets.

@TheNamesJamesW
Created November 12, 2016 20:25
Show Gist options
  • Save TheNamesJamesW/beb0a5016ee88bbfdcf562e5e0e412a0 to your computer and use it in GitHub Desktop.
Save TheNamesJamesW/beb0a5016ee88bbfdcf562e5e0e412a0 to your computer and use it in GitHub Desktop.
A filter that can be applied on a Sequence but stored (and passed around) independently.
/**
Defines a filter that can be applied on a `Sequence` instance but stored (and passed around) independently.
Useful if switching filters frequently, creating dynamic subsets of a given dataset and passing between abstraction levels (i.e. View level could create this and pass to the Model)
Example:
```
let strings = ["c", "b", "b", "b", "a"]
var predicate: Filter<String, [String]>
predicate = .compound(.sort, .first(3))
predicate.apply(to: strings)
// ["a", "b", "b"]
```
The following two `Filter`s are equivalent
```
predicate = .compound(.equals("b"), .first(2))
predicate.apply(to: strings)
// ["b", "b"]
predicate = Filter.equals("b").compounding(.first(2))
predicate.apply(to: strings)
// ["b", "b"]
```
However, this one is not, as `.first(2)` is applied **before** `.equals("b")`
```
predicate = Filter.first(2).compounding(.equals("b"))
predicate.apply(to: strings)
// ["b"]
```
*/
enum Filter<Element, C: SubSequenceConvertible & ArrayConvertibleSequence> where C.Iterator.Element == Element, C.SubSequence: Sequence, C.SubSequence.Iterator.Element == Element {
case first(Int)
case last(Int)
case filterWhere((Element) -> Bool)
case sortWhere((Element, Element) -> Bool)
indirect case compound(Filter, Filter)
}
extension Filter {
func apply(to input: C) -> C {
switch self {
case .first(let limit):
return C(input.prefix(limit))
case .last(let limit):
return C(input.suffix(limit))
case .filterWhere(let predicate):
return C(input.filter(predicate))
case .sortWhere(let predicate):
return C(input.sorted(by: predicate))
case .compound(let a, let b):
return b.apply(to: a.apply(to: input))
}
}
}
extension Filter {
func compounding(_ other: Filter) -> Filter {
return .compound(self, other)
}
}
extension Filter where Element: Equatable {
static func equals(_ other: Element) -> Filter {
return Filter.filterWhere({
$0 == other
})
}
}
extension Filter where Element: Comparable {
static var sort: Filter {
return Filter.sortWhere({
$0 < $1
})
}
}
extension Filter {
/**
Determines generic types automatically. Useful if you don't know the exact type of `Sequence` being used or what its `Element` is
```
let dictionary = ["a" : 1,
"b" : 2,
"c" : 3]
var x = Filter.create(.first(1), for: dictionary)
// `x` is now of Type `Filter<(String, Int), Dictionary<String, Int>>`
// ...
x = .first(2)
// ...
x.apply(to: dictionary)
```
- Parameter filter: The filter you want to apply
- Parameter for: The sequence whose Type you want the filter to apply to
*/
static func create(_ filter: Filter, for _: C) -> Filter {
return filter
}
}
// MARK: - Required protocols and conformance
/**
Defines a `Sequence` that can be created from a `SubSequence` of itself
```
let array = [1,2,3,4]
// Array<Int>.Type
let toSubArray = array[0..<4]
// ArraySlice<Int>.Type
let andBack = Array(toSubArray)
// Array<Int>.Type
```
*/
protocol SubSequenceConvertible: Sequence {
init(_ subSequence: SubSequence)
}
/**
Defines a `Sequence` that can be created from an `Array` of its `Element`s
```
let dictionary = ["a" : 1,
"b" : 2,
"c" : 3]
// Dictionary<String, Int>.Type
let filtered = dictionary.filter {
$0.value <= 2
}
// Array<(String, Int)>.Type
let dictAgain = Dictionary(filtered)
// Dictionary<String, Int>.Type
```
*/
protocol ArrayConvertibleSequence: Sequence {
init(_ array: Array<Iterator.Element>)
}
extension Array: SubSequenceConvertible, ArrayConvertibleSequence {}
extension Dictionary: SubSequenceConvertible, ArrayConvertibleSequence {
internal init(_ subSequence: Slice<Dictionary<Key, Value>>) {
self.init(subSequence)
}
init(_ elements: [Element]) {
self.init()
for (k, v) in elements {
self[k] = v
}
}
}
//MARK: - Filter examples
let strings = ["c", "b", "b", "b", "a"]
var predicate: Filter<String, [String]>
predicate = .compound(.sort, .first(3))
predicate.apply(to: strings)
// ["a", "b", "b"]
predicate = .compound(.equals("b"), .first(2))
predicate.apply(to: strings)
// ["b", "b"]
predicate = Filter.equals("b").compounding(.first(2))
predicate.apply(to: strings)
// ["b", "b"]
predicate = Filter.first(2).compounding(.equals("b"))
predicate.apply(to: strings)
// ["b"]
//MARK: - ArrayConvertibleSequence example
let array = [1,2,3,4]
// Array<Int>.Type
let toSubArray = array[0..<4]
// ArraySlice<Int>.Type
let andBack = Array(toSubArray)
// Array<Int>.Type
//MARK: - SubSequenceConvertible example
let dictionary = ["a" : 1,
"b" : 2,
"c" : 3]
// Dictionary<String, Int>.Type
let filtered = dictionary.filter {
$0.value <= 2
}
// Array<(String, Int)>.Type
let dictAgain = Dictionary(filtered)
// Dictionary<String, Int>.Type
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment