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
    }
}