【问题标题】:Swift: sorting function for ordering paths (needs to be fast!)Swift:排序路径的排序功能(需要快!)
【发布时间】:2016-03-02 21:44:46
【问题描述】:

注意:我找到了一种方法来做我正在寻找的事情,代码如下。我现在需要尽可能地优化它,因为在一个包含 8k 个对象的数组上执行仍然需要 20 多秒

我想对具有path 属性并代表用户系统上的文件的对象进行Array 排序。我想要达到的目标如下:

考虑这个目录结构

├── a
│   └── a.ext
├── b
│   ├── b.ext
│   ├── c.ext
│   └── d
│       └── d.ext
├── c
│   └── e.ext
├── f.ext
└── g.ext

当使用 NSFileManager 读取目录的内容时,我得到的路径不是我想要的顺序。因此,我需要对数组进行排序以获得顺序:

./f.ext
./g.ext
./a/a.ext
./b/b.ext
./b/c.ext
./b/d/d.ext
./c/e.ext

排序应该不区分大小写。对于每个文件夹,首先按字母顺序显示文件,然后显示文件夹,并且在每个文件夹中都适用相同的规则。

有没有办法对数组进行排序以获得所需的顺序?排序函数是什么样子的?

谢谢!

编辑

我自己找到了解决方案,但我现在想知道是否可以加快速度?

    func sorter(obj1: FilesListData, obj2: FilesListData) -> Bool {
        var result: Bool = false

        let url1 = NSURL(fileURLWithPath: obj1.path)
        let url2 = NSURL(fileURLWithPath: obj2.path)
        guard let c1 = url1.pathComponents else {
            return result
        }
        guard let c2 = url2.pathComponents else {
            return result
        }

        for i in 0..<min(c1.count, c2.count) {
            if c1[i] == c2[i] {
                continue
            } else {
                if i+1 == c1.count {
                    if i+1 == c2.count {
                        result = c1[i].lowercaseString < c2[i].lowercaseString
                        break
                    } else {
                        result = true
                        break
                    }
                } else {
                    if i+1 == c2.count {
                        result = false
                        break
                    } else {
                        result = c1[i].lowercaseString < c2[i].lowercaseString
                        break
                    }
                }
            }
        }

        return asc ? result : !result //asc is a variable indicating if the order should be ascending or not
    }

【问题讨论】:

  • 我认为你需要扩展你对 NSURL 组件的使用,并将你的整体包装在一个带有自定义比较器的类中。此外,有些种类确实需要时间来完成。您可以通过在插入时使用冒泡排序来加快速度,但比较字符串总是很糟糕。我也不确定免费排序会获得哪些优化。
  • 我在我的问题中添加了一些代码,这应该有助于开始优化讨论;)

标签: arrays swift sorting path tree


【解决方案1】:

这是一种简洁的方法,我认为应该足够快:

[编辑:调整以显示如何将其与对象列表一起使用]

class FileObject
{
   var path:String
   init(_ path:String)
   { self.path = path }
}

var files:[FileObject] = 
  [
    FileObject("./b/c.ext"),
    FileObject("./f.ext"),
    FileObject("./g.ext"),
    FileObject("./a/a.ext"),
    FileObject("./b/b.ext"),
    FileObject("./b/d/d.ext"),
    FileObject("./c/e.ext")
  ]

files = files.map({ ($0, $0.path.rangeOfString("/", options: .BackwardsSearch)!) })
             .map({ ($0, $0.path.stringByReplacingCharactersInRange($1, withString: "/ ")) })
             .map({ ($0, $1.lowercaseString) })
             .sort({ $0.1 < $1.1 })
             .map({  $0.0 })


files.forEach({ print($0.path) })

// prints:
//
// ./f.ext
// ./g.ext
// ./a/a.ext
// ./b/b.ext
// ./b/c.ext
// ./b/d/d.ext
// ./c/e.ext

// Benchmark for 8000 gives 0.134 sec.

【讨论】:

  • 是的!哦,我的上帝,这超级快速,如此简洁,正是我想要的,非常感谢!
  • 注意:如果你需要添加到数组中并经常使用它,你可以通过在你的类中存储一个“sortString”来加快速度(使用与 .map 调用相同的 3 个操作构建在我的示例中)并使用简单的排序($0.sortString
  • 好主意,但根据我的测试,整个排序的时间与简单排序几乎相同(在生产中,我没有在 XCTest 中尝试)我注意到 Swift 编译器有时会出现问题使用 mapsort 的简短版本,所以我用长格式闭包重写了所有内容,包括参数和返回类型
【解决方案2】:

这有帮助吗?用法:

printContents("/Users/paulvs/Desktop/my directory")

代码:

func printContents(path: String) {
    let fileManager = NSFileManager()
    let URL = NSURL(fileURLWithPath: path)
    let keys = [NSURLIsDirectoryKey]
    let enumerator = fileManager.enumeratorAtURL(URL, includingPropertiesForKeys: keys, options: NSDirectoryEnumerationOptions(rawValue: 0), errorHandler: { url, error -> Bool in
        return true
    })!

    var filePaths = [String]()
    var directoryPaths = [String]()

    while let url = enumerator.nextObject() as? NSURL {
        var path: AnyObject?
        var isDirectory: AnyObject?
        do {
            try url.getResourceValue(&isDirectory, forKey: NSURLIsDirectoryKey)
            try url.getResourceValue(&path, forKey: NSURLPathKey)

            if isDirectory as? Bool == true {
                directoryPaths.append(path as! String)
                enumerator.skipDescendants()
            } else {
                filePaths.append(path as! String)
            }
        } catch _ {

        }
    }

    filePaths.sort().forEach({ filePath in
        print(filePath)
    })

    directoryPaths.sort().forEach({ directoryPath in
        printContents(directoryPath)
    })
}

【讨论】:

  • 这可能会有所帮助,但它是否比我在编辑的问题中提出的更快?使用这种方法,我必须按照读取的顺序为每个文件分配一个 ID,然后根据该 ID 进行排序,因为 Array 的顺序会随着时间而改变。我需要能够在其生命周期内对其进行多次排序。
  • 您可以使用 XCTest 对您的代码进行基准测试并进行比较。
  • 好的,经过一些基准测试后,这个方法非常快,但它并没有真正做到我在问题中提出的要求:对具有路径属性(字符串)的对象数组进行排序
  • 我知道它并没有准确地回答您的问题,但由于您使用的是NSFileManager,我认为您可以在阅读路径时进行排序。
  • 你说得对,这对初读会有帮助。问题是,数组随后显示在表格视图中,并且文件名可以在程序执行过程中更改。我希望能够在重命名文件时重新排序表(数组),而无需重新读取文件系统中的所有文件。此外,用户可以选择通过另一列对表格进行排序,当他们想根据路径对其进行排序时,我也需要能够对数组进行重新排序。无论如何,谢谢我保留此代码以供以后使用/优化!
【解决方案3】:

因此,您希望按最外层目录作为第一个排序键对路径进行排序,然后将下一个内部目录作为第二个排序键,直到文件名作为最后一个排序键(任何级别的目录都没有排序到最佳)?

如果混合中只有一个目录,这将非常简单。获取目录。获取文件名。创建一个排序描述符,使用这两个作为排序键进行排序。如果您需要处理更深层次的目录结构,它会变得更复杂一些。

【讨论】:

  • 我可以设计的规则是这样的:查看要比较的两个文件中每个文件的第一个路径组件, 1. 如果它们相等,继续查看下一个路径组件并开始结束,2. 如果它们不同,则(2.1 如果两个组件都代表文件,则按字母顺序排序,2.2 如果一个组件是文件,另一个组件是目录,则文件排在第一位,2.3 如果两者都是文件夹组件,则按字母顺序排序)。我在我的问题中发布了一些代码,我们需要对其进行优化! ;)
猜你喜欢
  • 2015-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-19
  • 1970-01-01
  • 2011-02-11
  • 2017-10-07
  • 2018-10-24
相关资源
最近更新 更多