Swift 创建最高效的自定义过滤器函数(针对我的用例)
所以我想过滤的数据是:Swift 创建最高效的自定义过滤器函数(针对我的用例),swift,function,generics,Swift,Function,Generics,所以我想过滤的数据是:[Movie] 每部电影都是: struct: Movie { let title: String let tag: String let price: Int let genre: String } 这里有更多的属性,但为了保持简单,我就到此为止 现在,我想为用户提供一些好的搜索选项,例如: 所有低于10美元和的电影都有“恐怖”类型 所有低于10美元和的电影都有一个“恐怖”类型和一个新标签 因此,用户可以输入一个搜索条件,该搜索条件将给出一些
[Movie]
每部电影都是:
struct: Movie {
let title: String
let tag: String
let price: Int
let genre: String
}
这里有更多的属性,但为了保持简单,我就到此为止
现在,我想为用户提供一些好的搜索选项,例如:
- 所有低于10美元和的电影都有“恐怖”类型
- 所有低于10美元和的电影都有一个“恐怖”类型和一个新标签
below10 {
genre==horror {
tag==new {
}
}
}
以下是我到目前为止的情况:
func dynamicFilter(title: String = "", genre: String = "", tag: String = "", searchCriterion x: String) -> Bool {
if title.isEmpty { return false }
if title.hasPrefix(x) { return true }
if genre.isEmpty { return false} .... //And repeat
}
但该函数有一个缺陷,它会在不需要的位置返回。。。这里需要泛型吗?可能由一个高阶函数调用多个函数?如果要检查的类型仅为
String
或Int
创建一个自定义协议,将值
类型约束为String
或Int
protocol StringOrInt {}
extension Int : StringOrInt {}
extension String : StringOrInt {}
然后创建一个带有键路径
、值为StringOrInt
和比较结果
的过滤器
结构,这是
和=
的常见类型
struct Filter {
let value : StringOrInt
let keyPath : PartialKeyPath<Movie>
let ordered : ComparisonResult
}
这将过滤所有具有价格
<10和类型
=动作
最后,创建一个
filterMovies
函数,将过滤器应用于标准库的filter
函数中的Movie
项。Int
值被转换为NSNumber
,以便能够使用比较
并与给定的比较结果
func filterMovies(_ movies : [Movie], by criteria : [Filter]) -> [Movie] {
return movies.filter { movie -> Bool in
for filter in criteria {
let value = filter.value
if let stringValue = value as? String {
if (movie[keyPath: filter.keyPath] as! String).compare(stringValue) != filter.ordered { return false }
} else if let intValue = value as? Int {
let numValue = NSNumber(value: intValue)
let movieInt = movie[keyPath: filter.keyPath] as! Int
if (NSNumber(value: movieInt)).compare(numValue) != filter.ordered { return false }
} else { return false }
}
return true
}
}
我甚至不完全确定是否需要一系列标准。我对函数式编程不感兴趣,但它确实有一些很棒的想法。在本例中,使用高阶函数,尽管这意味着返回函数的函数。我不确定您是否需要定义函数来实现这一点。只需显式地构建闭包 首先将筛选器类型定义为一个闭包,该闭包接受
电影
,并返回Bool
:
typealias电影过滤器=(电影)->Bool
并定义一个基本的“passthrough”过滤器,该过滤器始终返回true
作为起点
var-filter:MovieFilter={{uu}
当用户选择“10美元以下的电影”时,您会执行以下操作
filter={filter($0)&&$0.price
然后,当他们将体裁限制添加到“恐怖”中时,你可以用类似的方式添加:
filter={filter($0)&&$0.genre==userGenre}
等等
应用过滤器很简单:
let result=movies.filter(过滤器)
到目前为止,假设所有过滤器都是“和”过滤器。如果它们都是“或”过滤器,这将是完全相同的过程,但是您的初始过滤器需要返回false
,当然还需要使用|
作为组合运算符
如果您让用户以更复杂的方式建立标准,您仍然可以这样做,但是您必须保存不同的子过滤器并将它们组合起来
例如,用户可能有点喜欢恐怖片,但喜欢科幻小说,所以他们愿意为此支付更多的费用。他们的搜索将类似于10美元以下的所有恐怖电影或20美元以下的所有科幻电影
你会像上面一样建立你的恐怖标准,并分别建立科幻小说的临界点,同样的方法,然后将它们结合起来:
let filter:MovieFilter={firstFilter($0)| | secondFilter($0)}
让结果=电影。过滤器(过滤器)
而不是返回true
您应该为每个条件设置一个标志,然后在结束时检查所有标志,或者仅在负条件下工作,并直接返回false,并且仅在最后一个条件返回trueah gotcha!类似于[“title”:“a”,“genre”:“horror”,“tag”:“],可能还有一个更高阶的函数,在字典上迭代,并返回一些类型。当我说flag时,我指的是一个局部变量let titleFlag=title.hasPrefix(x)
以此类推。
func filterMovies(_ movies : [Movie], by criteria : [Filter]) -> [Movie] {
return movies.filter { movie -> Bool in
for filter in criteria {
let value = filter.value
if let stringValue = value as? String {
if (movie[keyPath: filter.keyPath] as! String).compare(stringValue) != filter.ordered { return false }
} else if let intValue = value as? Int {
let numValue = NSNumber(value: intValue)
let movieInt = movie[keyPath: filter.keyPath] as! Int
if (NSNumber(value: movieInt)).compare(numValue) != filter.ordered { return false }
} else { return false }
}
return true
}
}