Сортировка 2D массива в New Swift, предыдущая сортировка не работает
Привет и большое спасибо за вашу помощь. Я новичок, чтобы быстро и тщательно изучить другие ответы, но они все не работают для меня. У меня есть пример данных в формате:
["10-24-2015", "124", "Да", "Нет", "Нет", "Да", "Да", "Нет", "Нет"]
Есть только 3 записи с разными датами и значениями, и все они в основном массиве. Он уже записан в файл, и поэтому я начинаю код ниже с загруженного файла. Все, что я хотел бы сделать, это отсортировать массивы по дате, которая является первым значением в каждом массиве.
Проблема с большинством уже опубликованных ответов заключалась в том, что они использовали более старые коды, и я сделал все возможное, чтобы попытаться преобразовать их безуспешно. Я использовал бета-версию XCode 7.1, пока пару дней назад не вышел XCode 7.1. Ниже приведен пример, который дает мне не ошибку компилятора, а большую ошибку исключения приложения для выхода. Я также включил другие, которые я попробовал, которые были близки как комментарии и ошибки, которые я получил.
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
}
Забыл упомянуть, что все коды работают без проблем, когда я использую образец одного массива. Но сделайте это многомерным, тогда я получу ошибки исключения.
1 ответ
Здесь есть несколько вопросов для решения:
Сортировка многомерного массива
Для тестирования я заполнил ваш resultsArray
отформатировать несколькими фиктивными строками:
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"],
]
Теперь сортировка в Swift довольно проста: вызовите sort
функция и передать замыкание, которое принимает два (внешних) элемента массива и возвращает true
если первое должно быть отсортировано до второго. Ваши элементы имеют тип [String]
То есть массивы строк, поэтому для сортировки по одному из элементов подмассива нужно просто найти их и сравнить.
let sorted = resultsArray.sort { left, right in
left[0] < right[0]
}
// or a shorter syntax:
let sorted2 = resultsArray.sort { $0[0] < $1[0] }
Но это, вероятно, не то, что вы хотите.
Сортировка по дате
Вышеуказанные сортировки строк лексикографически. Это означает, что вы получите такой результат:
["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"]
Ваш формат даты - месяц-день-год, поэтому, сравнивая их как строки, вы не получаете временное упорядочение ("08-24-2015" предшествует "10-24-2014", потому что первое начинается с "08").
Лучший способ сделать это - проанализировать даты, чтобы вы могли сравнить их как даты. Вот выстрел в этом:
// 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
}
Это сортирует так, как вы ожидаете. Вы, вероятно, можете поправиться, хотя.
Использование модельного объекта
Вы, вероятно, захотите сделать больше с ними, так что это может помочь обработать строки как объекты, где каждое поле имеет некоторый смысл. Например:
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
}
}
}
}
Теперь вы можете иметь реальные объекты для каждого результата, чтобы вы могли лучше отслеживать, что к чему в вашем следующем коде. Давайте сделаем массив Result
объекты и отсортировать его:
let results = resultsArray.map(Result.init).sort {
$0.date.compare($1.date) == .OrderedAscending
}
Подождите, сколько раз мы делаем это?
Вы делаете всю свою загрузку и сортировку данных (и разбор даты, и преобразовываете в Result
объекты, если вы следовали моему совету выше) все в tableView(_:cellForRowAtIndexPath:)
метод. Этот метод вызывается один раз для каждой строки в вашей таблице... что, вероятно, связано с загрузкой данных в tableView(_:numberOfRowsInSection:)
Метод - это число результатов в вашем файле данных. Это означает, что вы выполняете одну и ту же работу снова и снова и снова и замедляете первоначальную загрузку таблицы (а также любые последующие перезагрузки и прокрутку при переполнении кэша строк).
Переместите код загрузки, анализа и сортировки данных в другое место, которое выполняется только один раз (или один раз каждый раз, когда вы меняете базовый файл данных и хотите перезагрузить его обновленное содержимое). Может быть viewDidLoad
, может быть, вспомогательный метод, который вы можете вызвать позже. Пусть он сохранит свои результаты в свойстве экземпляра вашего контроллера табличного представления. Тогда вы можете обратиться к этим результатам гораздо быстрее, когда вам нужно.
Например (сокращено с помощью некоторого псевдокода):
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
}
}