在新Swift中对2D数组进行排序,先前的排序不起作用 [英] Sort 2D Array in New Swift, previous sort not working

查看:88
本文介绍了在新Swift中对2D数组进行排序,先前的排序不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您好,非常感谢您的帮助。我是新手,已经对其他答案进行了详尽的研究,但它们都不适合我。我有以下格式的示例数据:


[ 2015年10月24日, 124,是,否 ,否,是,是, NA, NA]


只有3个条目不同的日期和值,它们都在一个主数组中。它已经被写入文件了,所以我从加载的文件开始下面的代码。我要做的就是按日期对数组进行排序,这是每个数组中的第一个值。



已经发布的大多数答案的问题在于它们使用了较旧的代码,我尽力尝试将其转换失败。我一直在使用XCode 7.1 Beta,直到几天前XCode 7.1发布。我得到的最接近的值是以下值,它没有编译器错误,但有一个很大的退出应用程序异常错误。我还包括我尝试过的其他内容,这些内容与评论和我收到的错误很接近。

  var resultsArray = [[String]]()


覆盖func tableView( tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell {

let cellIdentifier = resultsViewCell
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier,forIndexPath:indexPath)为! resultsTableViewCell

let resultsdocumentsURL = NSFileManager.defaultManager()。URLsForDirectory(.DocumentDirectory,inDomains:.UserDomainMask)[0]
let resultsfileURL = resultsdocumentsURL.URLByAppendingPathComponent( ResultsPlist.plist)

resultsArray = NSKeyedUnarchiver.unarchiveObjectWithFile(resultsfileURL.path!)as!数组


let排序:NSSortDescriptor = NSSortDescriptor(key: date,ascending:false)
let sortedArray:NSArray =(resultsArray as NSArray).sortedArrayUsingDescriptors([Sort] )


// var sortedArray = resultsArray.sort {$ 0.localizedCaseInsensitiveCompare($ 1)== NSComparisonResult.OrderedAscending}
//错误:类型[String]的值没有成员'localizedCaseInsensitiveCompare'

// var sortedArray = resultsArray.sort({$ 0.0> $ 1.0})
//错误:类型[[String]'的值没有成员' 0'


返回单元格

}

忘了提及当我使用示例单个数组时,所有代码都没问题。但是将其设为多维,然后会出现异常错误。

解决方案

这里有几个问题要解决:



排序多维数组



为了测试,我填写了您的 resultsArray 带有几行伪造行的格式:

  let resultsArray = [
[ 10-24-2015, 124,是,否,否,是,是, NA, NA],
[ 2015年9月23日, 125,是,否,否,否,是,不适用,不适用],
[ 10-21-2015, 121,是,否,是,是,是, NA, NA],
[ 08-24-2015, 141,是,是,否,是,是, NA, NA],
[ 10-24-2014, 199,是,否,否,是,否,不适用,不适用],
]

现在,在Swift中对它进行排序非常简单:调用 sort 函数,并传递一个接受两个(外部)数组元素并返回 true <的闭包。 / code>如果第一个应该在第二个之前排序。您的元素的类型为 [String] -即字符串数组-因此,要按子数组元素之一进行排序,您只需要查找它们并进行比较即可

  let sorted = resultsArray.sort {左,右
left [0]< right [0]
}
//或更短的语法:
let sorted2 = resultsArray.sort {$ 0 [0]< $ 1 [0]}

但这可能不是您想要的。



按日期排序



以上按字典顺序对字符串进行排序。这意味着您将获得如下结果:

  [ 08-24-2015, 141,是 ,是,否,是,是,不适用,不适用] 
[ 09-23-2015, 125,是,否, 否,否,是,不适用,不适用]
[ 2015年10月21日, 121,是,否,是,是,是,不适用,不适用]
[ 10-24-2014, 199,是,否,否,是,否 , NA, NA]
[ 10-24-2015, 124,是,否,否,是,是,不适用 , NA]

您的日期格式为月日年,因此将它们比较为您没有时间顺序的字符串( 08-24-2015在 10-24-2014之前,因为第一个以 08开头)。



<更好的方法是解析日期,以便可以将它们作为日期进行比较。

  //解析实际日期
let formatter = NSDateFormatter()
formatter .dateFormat = MM-dd-yyyy
let sorted3 = resultsArray.sort {在
中左移,右移let leftDate = formatter.dateFromString(left [0])!
让rightDate = formatter.dateFromString(right [0])!
// NSDate没有<运算符,所以请使用Foundation比较方法
return leftDate.compare(rightDate)== .OrderedAscending
}

这是您期望的方式。但是,您可能会变得更好。



使用模型对象



您可能想要使用这些功能做更多的事情,因此将行视为每个字段都有一定意义的对象可能会有所帮助。例如:

  struct结果{
设置日期:NSDate
设置编号:Int
让响应:[布尔?]
init(array:[String]){
date = formatter.dateFromString(array [0])!
数字= Int(array [1])!
响应= array [2 ..< array.count] .map {
开关$ 0 {
情况为是:返回true
情况为否:返回false
默认值:返回nil
}
}
}
}

现在,每个结果都可以有真实的对象,因此您可以更好地跟踪以后的代码中的内容。让我们制作一个 Result 对象的数组,并对它进行排序:

  let结果= resultsArray.map(Result.init).sort {
$ 0.date.compare($ 1.date)== .OrderedAscending
}



等等,我们要执行几次?



您正在完成所有数据加载和排序(以及日期解析,并转换为 Result 对象,如果您遵循了我的上述建议),则全部放在 tableView(_:cellForRowAtIndexPath:)方法。此方法对表中的每一行都调用一次 ...,这大概是由于您还将数据加载到了 tableView(_:numberOfRowsInSection:)方法,是数据文件中结果的数量。这意味着您要一次又一次地执行相同的工作,并减慢表的初始加载(以及随后的所有重新加载,并在行高速缓存溢出时滚动)。



将数据加载,解析和排序代码移动到仅执行一次(或每次更改基础数据文件并希望重新加载其更新的内容一次)的地方。也许 viewDidLoad ,也许是一种实用方法,您以后可以再次调用它。将其结果保存在表视图控制器的instance属性中。然后,您可以随时根据需要更快地引用这些结果。



例如(用一些伪代码缩写):

 类MyTableViewController:UITableViewController {

let cellIdentifier = resultsViewCell
var结果:[结果]!

重写功能viewDidLoad(){
let resultsdocumentsURL = NSFileManager.defaultManager()。URLsForDirectory(.DocumentDirectory,inDomains:.UserDomainMask)[0]
let resultsfileURL = resultsfilesURL.URLByAppendingPathComponent ( ResultsPlist.plist)
保护让resultArray = NSKeyedUnarchiver.unarchiveObjectWithFile(resultsfileURL.path!)设为? [[String]]
else {fatalError( plist中的意外结果)}
结果= resultsArray.map(Result.init).sort {
$ 0.date.compare($ 1。 date)== .OrderedAscending
}
}

覆盖func tableView(tableView:UITableView,numberOfRowsInSection部分:Int)-> Int {
return results.count
}

覆盖func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier,forIndexPath:indexPath)如! ResultsTableViewCell
let result = results [indexPath.row]
cell.textLabel?.text = result.description //或显示结果
}
}


Hi and thank you very much for your help. I'm new to swift and have thoroughly researched the other answers but they all are not working for me. I have sample data in the format:

["10-24-2015", "124", "Yes", "No", "No", "Yes", "Yes", "NA", "NA"]

There are only 3 entries with varying dates and values and they're all in a master array. It's been written to a file already and so I start my code below from a loaded file. All I would like to do is to sort the arrays by date which is the first value in each array.

The problem with most of the answers already posted were that they used older codes and I did my best to try to convert them unsuccessfully. I had been using XCode 7.1 beta until XCode 7.1 came out a couple of days ago. The closest I've got is below which gives me no compiler error but a big quit app exception error. I also included other ones that I tried that were close as comments and the errors I got.

var resultsArray = [[String]]()


override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cellIdentifier = "resultsViewCell"
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! resultsTableViewCell

    let resultsdocumentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let resultsfileURL = resultsdocumentsURL.URLByAppendingPathComponent("ResultsPlist.plist")

    resultsArray = NSKeyedUnarchiver.unarchiveObjectWithFile(resultsfileURL.path!) as! Array


    let Sort: NSSortDescriptor = NSSortDescriptor(key: "date", ascending: false)
    let sortedArray: NSArray = (resultsArray as NSArray).sortedArrayUsingDescriptors([Sort])


    // var sortedArray = resultsArray.sort {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }  
    // Error: Value of type '[String]' has no member 'localizedCaseInsensitiveCompare'

    // var sortedArray = resultsArray.sort({ $0.0 > $1.0 })
    // Error: Value of type '[String]' has no member '0'


    return cell

}

Forgot to mention that all the codes work no problem when I use a sample single array. But make it multidimensional then I get my exception errors.

解决方案

There are several issues to address here:

Sorting a Multidimensional Array

For testing, I've filled out your resultsArray format with a few bogus rows:

let resultsArray = [
    ["10-24-2015", "124", "Yes", "No", "No", "Yes", "Yes", "NA", "NA"],
    ["09-23-2015", "125", "Yes", "No", "No", "No", "Yes", "NA", "NA"],
    ["10-21-2015", "121", "Yes", "No", "Yes", "Yes", "Yes", "NA", "NA"],
    ["08-24-2015", "141", "Yes", "Yes", "No", "Yes", "Yes", "NA", "NA"],
    ["10-24-2014", "199", "Yes", "No", "No", "Yes", "No", "NA", "NA"],
]

Now, sorting this in Swift is pretty simple: call the sort function, and pass a closure that takes two (outer) array elements and returns true if the first should be sorted before the second. Your elements are of type [String] — that is, arrays of strings — so to sort by one of the sub-array elements you just need to look them up and compare them.

let sorted = resultsArray.sort { left, right in
    left[0] < right[0]
}
// or a shorter syntax:
let sorted2 = resultsArray.sort { $0[0] < $1[0] }

But this probably isn't what you want.

Sorting by Date

The above sorts strings lexicographically. This means that you get a result like this:

["08-24-2015", "141", "Yes", "Yes", "No", "Yes", "Yes", "NA", "NA"]
["09-23-2015", "125", "Yes", "No", "No", "No", "Yes", "NA", "NA"]
["10-21-2015", "121", "Yes", "No", "Yes", "Yes", "Yes", "NA", "NA"]
["10-24-2014", "199", "Yes", "No", "No", "Yes", "No", "NA", "NA"]
["10-24-2015", "124", "Yes", "No", "No", "Yes", "Yes", "NA", "NA"]

Your date format is month-day-year, so when you compare these as strings you don't get a temporal ordering ("08-24-2015" comes before "10-24-2014" because the first one starts with "08").

The better way to do this is to parse the dates so that you can compare them as dates. Here's a shot at that:

// parse actual dates
let formatter = NSDateFormatter()
formatter.dateFormat = "MM-dd-yyyy"
let sorted3 = resultsArray.sort { left, right in
    let leftDate = formatter.dateFromString(left[0])!
    let rightDate = formatter.dateFromString(right[0])!
    // NSDate doesn't have a < operator, so use the Foundation compare method
    return leftDate.compare(rightDate) == .OrderedAscending
}

This sorts the way you expect. You can probably get better, though.

Using a Model Object

You're probably going to want to do more with these, so it might help to treat the rows as objects where each field has some meaning. For example:

struct Result {
    let date: NSDate
    let number: Int
    let responses: [Bool?]
    init(array: [String]) {
        date = formatter.dateFromString(array[0])!
        number = Int(array[1])!
        responses = array[2..<array.count].map {
            switch $0 {
                case "Yes": return true
                case "No": return false
                default: return nil
            }
        }
    }
}

Now you can have real objects for each result, so you can better keep track of what's what in your later code. Let's make an array of Result objects, and sort it:

let results = resultsArray.map(Result.init).sort {
    $0.date.compare($1.date) == .OrderedAscending
}

Wait, how many times are we doing this?

You're doing all your data loading and sorting (and date parsing, and transforming to Result objects, if you followed my advice above) all in the tableView(_:cellForRowAtIndexPath:) method. This method gets called once for every row in your table... which, presumably due to you also loading the data in the tableView(_:numberOfRowsInSection:) method, is the number of results in your data file. That means you're doing the same work again and again and again, and slowing down the initial load of your table (as well as any subsequent reloads, and scrolling when the row cache overflows).

Move your data loading, parsing, and sorting code to someplace that only executes once (or once every time you change the underlying data file and want to reload its updated contents). Maybe viewDidLoad, maybe a utility method you can call again later. Have it save its results in an instance property of your table view controller. Then you can refer to those results much more quickly, whenever you need.

For example (shortened with some pseudocode):

class MyTableViewController: UITableViewController {

    let cellIdentifier = "resultsViewCell"
    var results: [Result]!

    override func viewDidLoad() {
        let resultsdocumentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
        let resultsfileURL = resultsdocumentsURL.URLByAppendingPathComponent("ResultsPlist.plist")
        guard let resultsArray = NSKeyedUnarchiver.unarchiveObjectWithFile(resultsfileURL.path!) as? [[String]]
            else { fatalError("unexpected results from plist") }
        results = resultsArray.map(Result.init).sort {
            $0.date.compare($1.date) == .OrderedAscending
        }
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return results.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ResultsTableViewCell
        let result = results[indexPath.row]
        cell.textLabel?.text = result.description // or however you display a result
    }
}

这篇关于在新Swift中对2D数组进行排序,先前的排序不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆