Сортировка проста, если она выполняется по одному критерию или одному свойству. В Swift уже есть функция для этого.
Приведем пример, в котором мы сортируем массив int.
Однако в некоторых случаях возникает необходимость сортировки по нескольким критериям или свойствам. Чтобы продемонстрировать это, давайте в качестве примера создадим структуру.
Здесь у нас есть простая структура BlogPost с заголовком записи и двумя статистическими данными - просмотром страницы и продолжительностью сессии.
А вот примерный набор данных.
Если вы хотите увидеть, какие посты пользуются хорошей популярностью, вы можете начать с сортировки по количеству просмотров страниц. Но, как вы видите, многие посты не так популярны и имеют одинаковое количество просмотров страниц. В этом случае необходим еще один критерий или свойство для дальнейшей сортировки.
Именно о такой сортировке по нескольким свойствам мы и поговорим в этой статье. Существует множество способов решения этой задачи. Я покажу самый простой подход без каких-либо дополнительных концепций. Вы можете сделать его настолько продвинутым, насколько захотите, как только поймете основы.
- Мы перебираем список критериев, начиная с самого важного (Первый).
- Если критерии порядка равны и мы не можем определить порядок, то переходим к следующему критерию.
- Если мы можем определить порядок между двумя объектами из критериев, то мы останавливаемся и возвращаем результат.
Если вам трудно понять псевдокод, не волнуйтесь. Я не являюсь профессиональным автором псевдокода. Следующий пример должен прояснить ситуацию.
Сортировка массива объектов по двум полям
Мы будем использовать тот же самый сценарий, о котором говорилось выше. Мы хотим отсортировать записи в блоге по производительности. Производительность мы определяем по количеству просмотров страниц (pageView), а если записи в блоге имеют одинаковые просмотры страниц, то мы используем длительность сессии (sessionDuration).
Вот структура BlogPost и пример данных, которые мы использовали в предыдущем примере.
То, как мы измеряем эффективность, можно перевести в этот код.
- Если записи в блоге имеют одинаковое количество просмотров страниц, мы используем длительность сессии.
- Если количество просмотров страниц не одинаково, мы можем определить порядок по просмотрам страниц. (Мы сортируем в порядке убывания).
Вот наш результат.
Сортировка массива объектов по трем полям
Как видите, выполнить сортировку по двум критериям очень просто. Давайте добавим в уравнение больше критериев. Если записи в блогах имеют одинаковые показатели, то мы будем сортировать их по имени.
Добавим в наши примеры больше записей в блогах.
Разницы между двумя и тремя критериями нет. Мы можем использовать ту же логику, что и раньше.
- Добавим еще одно if, чтобы проверить, имеют ли записи в блоге одинаковую продолжительность сессии, и отсортируем их по заголовку, если они набрали одинаковое количество просмотров страниц и продолжительность сессии.
Результаты:
Проблема
Мы можем использовать ту же логику для двух и трех критериев. Единственная проблема заключается в том, что чем больше критериев, тем больше вложенных if-else потребуется.
Вот пример множественных критериев, которые могут привести к пирамиде обреченности.
Сортировка массива объектов по N полям
Для решения пирамиды обреченности вернемся к рассмотренному ранее псевдокоду.
Приведенный выше код не является единственным способом решения подобной задачи, но ключ должен быть схожим. Суть в том, что мы упаковываем критерии в коллекцию, по которой можно работать в цикле.
- Я объявляю псевдоним AreInIncreasingOrder, который соответствует закрытию сортировки. Это улучшает читаемость при объявлении коллекции предикатов.
- Мы объявляем коллекцию предикатов.
- Перебираем предикаты в цикле.
- Здесь возникает сложный момент: мы хотим проверить, могут ли критерии определять порядок записей в блоге или нет. Но AreInIncreasingOrder возвращает булево значение. Как же нам проверить, является ли порядок одинаковым? Прежде чем ответить на этот вопрос, давайте рассмотрим определение AreInIncreasingOrder. AreInIncreasingOrder - это предикат, который возвращает true, если его первый аргумент должен быть упорядочен перед вторым аргументом; в противном случае - false. Таким образом, два аргумента находятся в равном порядке только в том случае, если оба аргумента не находятся в возрастающем порядке. Это означает, что наш предикат должен быть ложным независимо от порядка аргументов. Другими словами, чтобы считать порядок равным, lhs.pageView < rhs.pageView и rhs.pageView < lhs.pageView должны быть равны false. Именно это и означают наши !predicate(lhs, rhs) && !predicate(rhs, lhs).
- Если порядок равен, то мы переходим к следующему предикату.
- Если порядок не равен, то мы можем использовать этот предикат для определения порядка.
Результаты:
Заключение
Недавно я столкнулся с этим вопросом и нашел его интересным. Это простая задача, на решение которой у меня уходит некоторое время.
Методы, приведенные в этой статье, не привязаны к Swift. Вы можете применить их к любому языку по своему выбору. Вы можете усовершенствовать код, сделав его более общим, поддерживающим любые объекты или свойства, которые вам нужны, и я оставляю это на ваше усмотрение. Если вы придумаете что-то интересное, то можете поделиться со мной в Twitter. Я с удовольствием посмотрю на вашу реализацию.
Эта заметка является переводом оригинальной статьи Sarun Wongpatcharapakorn - https://sarunw.com/posts/how-to-sort-by-multiple-properties-in-swift/