【问题标题】:Filtering an array of object with priorities过滤具有优先级的对象数组
【发布时间】:2017-01-13 23:32:06
【问题描述】:

我正在尝试过滤和排列人员并按特定顺序获取结果。这意味着当用户输入 someString 时,我想首先展示他们的名字与那个 someString 匹配的人,然后从那个 someString 开始,然后是任何包含那个字符串的名字。 有什么办法可以使用 Array "Filter" 来完成,或者我必须在此之后执行另一个手动排序?

这是我的过滤代码:

self.filteredNames = self.names.filter({$0.name.lowercaseString.hasPrefix(text!) | $0.name.lowercaseString.containsString(text!) | $0.allEmails.lowercaseString.containsString(text!.lowercaseString) | $0.allNumbers.lowercaseString.containsString(text!.lowercaseString)})

【问题讨论】:

  • 您的过滤代码与您对问题的描述不符。看起来您正在过滤不同的字段,而不是对同一字段的不太具体的要求。
  • 其实,两者都是 .结果应按顺序排列:匹配字符串的名称、以该字符串开头的名称、包含该字符串的名称、包含该字符串的电子邮件、包含该字符串的电话号码。

标签: ios arrays swift filter


【解决方案1】:

您的代码与您的问题描述不符。我会按照你的描述去做,因为你的代码无论如何都没有做你想做的事。

您可以对每场比赛进行评分并按匹配分数排序:

struct Person {
    var name: String
}

let names = [
    Person(name: "John Smith"),
    Person(name: "Alan Johnson"),
    Person(name: "Jane Doe"),
    Person(name: "John")
]

let searchText = "John".lowercaseString // take this from you search box

let filteredNames = names.flatMap { p -> (Person, Int)? in
    var score = 0
    let name = p.name.lowercaseString

    if name == searchText {
        score += 3
    } else if name.hasPrefix(searchText) {
        score += 2
    } else if name.containsString(searchText) {
        score += 1  
    }

    return score == 0 ? nil : (p, score)
}.sort { $0.1 > $1.1 }
 .map { $0.0 }

print(filteredNames) // John, John Smith, Alan Johnson

它的作用如下:

  • flatMap 同时充当map 和filter。每个人都会根据您的标准分配一个匹配的分数。如果匹配分数为 0,则返回 nil,因此 flatMap 将其排除在外。
  • sort 按匹配分数排序结果
  • map 删除匹配分数,因此最终数组仅包含 Person

您可以将其扩展到电子邮件、电话号码等。只需使用评分机制即可。

【讨论】:

  • 这种方法需要对集合进行多次迭代(尽管至少后续的sort 和map 将在集合的子集上)。对于大型集合,特别是在有很多“点击”的情况下,单个 for 循环可能会执行得更好,对吧?
  • Donald Knuth:“过早的优化是万恶之源”。性能取决于很多因素:(a)要让您真正看到差异,数组中需要有数十万个项目; (b) 您的 for 循环类似地迭代多次; (c) 如果你将 Person 从 struct 更改为 class 会快很多,因为它只需要复制指针地址,而不是整个结构。只需使用对您最有意义的东西,然后对性能进行基准测试并优化
  • 关于(b),多次迭代发生在哪里?
  • 你的最后一行。当您将多个数组添加在一起时,您需要遍历它们以构建结果数组
  • 是的, - 连接可能与额外迭代的成本相似(据我所知)。此外,对于大量不同的“评分”操作,与创建多个数组相比,您的解决方案非常优雅
【解决方案2】:

使用sort:

func sortingFunction(obj1: YourClass, _ obj2: YourClass) -> Bool {
    return (str1.name.lowercaseString.hasPrefix(text!) && !obj2.name.lowercaseString.hasPrefix(text!)) ||
        (obj1.name.lowercaseString.containsString(text!) && !obj2.name.lowercaseString.containsString(text!)) ||
        (obj1.allEmails.lowercaseString.containsString(text!) && !obj2.allEmails.lowercaseString.containsString(text!)) &&
        (obj1.allNumbers.lowercaseString.containsString(text!) && !obj2.allNumbers.lowercaseString.containsString(text!))
}

self.filteredNames = self.names.sort(sortingFunction)

【讨论】:

  • 你仍然需要一个操作来删除任何与查询不匹配的元素,对吧?
  • 对,这只会对元素进行排序而不会删除任何元素,你的答案很适合
【解决方案3】:

filter 只能从集合中删除项目。它不能为你做任何订购操作。

您可能希望使用单遍数组来解决此问题,以便它可以正常执行。不要使用过滤器,而是做一些基本的事情:

var query = "test"

let allStrings = ["another test", "test", "testing", "random"]

var matchStrings = [String]()
var prefixStrings = [String]()
var containsStrings = [String]()

for str in allStrings {

    if query == str {
        matchStrings.append(str)
    } else if str.hasPrefix(query) {
        prefixStrings.append(str)
    } else if str.containsString(query) {
        containsStrings.append(str)
    }
}

let results = matchStrings + prefixStrings + containsStrings

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-29
    • 2021-11-21
    • 2022-06-10
    • 1970-01-01
    • 2016-02-14
    相关资源
    最近更新 更多