美文网首页
Swift: Realm自然排序的一种思路

Swift: Realm自然排序的一种思路

作者: BeauZ | 来源:发表于2019-02-07 08:36 被阅读0次

公司最近正在把iOS端的数据库从Core Data替换成Realm, 期间踩坑无数, 这里主要讲讲排序的问题.
Realm的排序和Core Data比起来简直就是弱爆了, 在Core Data中, 如果想做自然排序, 只需要在 sortDescriptors 中指定 selector 为 NSString.localizedStandardCompare 即可实现自然排序:

fetchRequest.sortDescriptors = [NSSortDescriptor(key: SortOptionFirstName, ascending: true, selector: #selector(NSString.localizedStandardCompare))]

但是Realm提供的对数据进行排序的 sorted(by: ) 方法简陋的一塌糊涂.

sorted(byKeyPath: "name", ascending: ascending)

如果用这种方法排序, 举个简单的例子 5个 Realm Objects 的name为(这也是我们想要的顺序):
Test, TEST, Test1, Test2, Test10
用以上方法排序出来的结果为:
Test, Test1, Test10, Test2, TEST
这个结果是不能接受的, 前两个的顺序其实并不重要, 主要是包含数字的name数字要按照人类理解的顺序来排

那么有什么方法可以实现自然排序呢? 在google搜索一番之后, 看到个令人叹服的解决方案:
其大概的意思是, 查找出字符串里所有的数字, 然后在数字的前面加上数字的位数

所以, 基于这种思想, 大概的解题思路是, 我们在需要用到自然排序的Realm数据模型里加上一条会被保存在Realm数据库中的属性nameForSort, nameForSort 会把name String 里所有的数字(包含小数)的整数部分的位数加在数字前面, 这样排序就不会有问题了.

具体代码如下:

首先是给String 添加一个function:
这里首先使用正则表达式获取String内所有的数字(包含整数和小数)的Range, 然后根据这个Range, 建立一个包含所有数字的数组, 在map整数部分的位数
这样, 所有数字的位置还有整数部分的位数都有了, 下面就是往String里所有数字前面插入整数位数, 这里我们倒着插入, 这样前面的位置就不会变了.

extension String {
    func fixedForSort() -> String {
        //use regular expression to fetch all digits (integer and pointing float) Range info
        let pattern = "\\d+\\.?\\d*"
        let regular = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
        let rangeResults = regular.matches(in: self, options: .reportProgress, range: NSRange(location: 0, length: self.count))
        //get the digits array based on the range info
        var digitsInString = [String]()
        for result in rangeResults {
            digitsInString.append((self as NSString).substring(with: result.range))
        }
        // map the digits array to Integer array then get the number of Integer
        let digitsCounts = digitsInString.map({String(Int(Double($0) ?? 0)).count})
        //reverse the range Results and digitsCounts for insert from back to front
        let rangeResultsReversed: [NSTextCheckingResult] = rangeResults.reversed()
        let digitsCountsReversed: [Int] = digitsCounts.reversed()
        var fixedString = self
        //insert the digitsCounts to the right position
        for i in 0..<rangeResultsReversed.count {
            fixedString.insert(contentsOf: "\(digitsCountsReversed[i])", at: String.Index(encodedOffset: rangeResultsReversed[i].range.location))
        }
        return fixedString.localizedLowercase
    }
}

我们有了这种方法之后, 在Realm object 数据模型中, 我们这样定义:

@objc dynamic private var real_name: String?
@objc dynamic private var nameForSort: String?
var name: String? {
  get {
    return real_name
  }
  set {
    real_name = newValue
    nameForSort = (newValue ?? "").fixedForSort()
  }
}

这样, 我们排序的时候用nameForSort来排序就OK了

当然, 这个idea还不成熟, 还有好多问题没考虑到, 比如, 如果俩个name中有两个整数部分相同, 但是小数部分不同, 这种情况下还是会排序不准. 不过在我们的APP中这种情况出现的概率基本没有, 所以就没多加考虑.
只是记录一种思路, 供需要的人参考

相关文章

网友评论

      本文标题:Swift: Realm自然排序的一种思路

      本文链接:https://www.haomeiwen.com/subject/ymswsqtx.html