Сортировка 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
    }
}
Другие вопросы по тегам