【问题标题】:swift: find-function in generic styleswift:通用风格的查找功能
【发布时间】:2021-04-01 23:03:46
【问题描述】:

我正在尝试编写自己的数据组织框架。

我有一个“根”类,它是基础,任何对象功能都应该来自它。例如,函数将是在数组中搜索对象。搜索功能应由子类继承。

我的问题:子类配备了更多属性。因此我想用泛型实现搜索功能。搜索功能应该基于属性中的值工作。例如Child(id: "one") 应该列出 id 为“one”的所有对象。 Child(id: "one", name: "Tom") 应该列出所有 id = one 和 name = Tom的对象。

如果函数要被类继承,是否可以用泛型实现搜索功能?

class ObjectController {
    
    func find<T>(data: Array<T>, searchParams: T ){
        dump(data)
        dump(searchParams)
        // Search logic
        /* Something like:
         for item in data {
             for i_attr in item {
                 for s_attr in searchParams {
                    if(s_attr == i_attr){
                        retrun item // found
                    }
                 }
             }
         }
         return nil // not found
         */
    }
}

class Root : ObjectController {
    var id: String?
    
    init(new_id: String?) {
        self.id = new_id
    }
}

class Child : Root {
    var name: String?
    
    init(new_id: String? = nil, new_name: String?) {
        self.name = new_name
        super.init(new_id: new_id)
    }
}

let Obj1 = Child(new_id: "eins", new_name: "Tom")
let Obj2 = Child(new_id: "zwei", new_name: "Tim")
let Obj3 = Child(new_id: "drei", new_name: "Max")

let data : Array<Child> = [Obj1, Obj2, Obj3]
print(data)

Child().find(data: data, searchParams: Child(new_name: "Tom"))

很高兴收到任何帮助

【问题讨论】:

    标签: swift generics


    【解决方案1】:

    您在这里要做的是构建一个通用“查询”,它可以告诉您给定对象是否匹配。我们稍后会担心如何构建查询,但如果你有这个,你可以编写这个函数过滤任何列表到传递一些查询列表的元素:

    extension Sequence {
        func matching(all queries: [Query<Element>]) -> [Element] {
            filter { element in queries.allSatisfy { $0.matches(element) } }
        }
    }
    

    现在我们只需要构建查询。基本上这是这个matches 函数的包装,它是通用的,但最终采用(Target) -&gt; Bool 的形式。我们只需要一种方式来表达“某个属性等于某个值”。

    为此的工具是 KeyPath。这使您可以谈论属性及其值,而不必知道属性实际上是什么。使用它,查询将如下所示:

    struct Query<Target> {
        let matches: (Target) -> Bool
        init<Value: Equatable>(keyPath: KeyPath<Target, Value>, equals value: Value) {
            matches = { $0[keyPath: keyPath] == value }
        }
    }
    

    您传递一个键路径和一个值,它将构造一个 matches 函数来测试它。

    给定一些数据:

    struct Child {
        var id: String
        var name: String
    }
    
    let data = [
        Child(id: "eins", name: "Tom"),
        Child(id: "zwei", name: "Tim"),
        Child(id: "drei", name: "Max"),
    ]
    

    您可以计算匹配对象的列表:

    data.matching(all: [Query(keyPath: \.id, equals: "eins"),
                        Query(keyPath: \.name, equals: "Tom")])
    
    // [{id "eins", name "Tom"}]
    

    注意matching(all:) 如何自动知道正确的查询目标(因为它必须匹配来自data 的元素),并且会防止您出现任何拼写错误:

    data.matching(all: [Query(keyPath: \.number, equals: "eins"), 
                        Query(keyPath: \.name, equals: "Tom")])
    // Value of type 'Child' has no member 'number'
    

    您可以通过编写更多Query.init 版本将其扩展到更多类型的比较,例如,isLessThan 查询:

    extension Query {
        init<Value: Comparable>(keyPath: KeyPath<Target, Value>, isLessThan value: Value) {
            matches = { $0[keyPath: keyPath] < value }
        }
    }
    

    通过编写新的matching 函数,您可以将其扩展为以其他方式使用查询,例如传递任何查询而不是所有查询:

    extension Sequence {
        func matching(any queries: [Query<Element>]) -> [Element] {
            filter { element in queries.contains { $0.matches(element) } }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多