Array.sort()
не для слабонервных. Особенно, когда речь идет о сортировке сложных структур данных.
Большинство руководств по JavaScript дают только краткое введение в Array.sort()
. Обычно это:
let myArray = [3, 5, 7, 1, 0]; console.log(myArray.sort());
Ого, спасибо, Капитан Очевидность! Но как отсортировать массив данных JSON? Нравится:
let keywords = [ { word: 'melon', occurrences: 2 }, { word: 'apple', occurrences: 1 }, { word: 'pear', occurrences: 4 }, { word: 'orange', occurrences: 2}, { word: 'plum', occurrences: 1 } ];
Это список ключевых слов, присвоенных массиву статей. Значение occurrences
указывает, скольким статьям назначено ключевое слово. Например, pear
очень популярный, он есть в 4 статьях.
Если мы хотим отсортировать список только по полю word
, вот простой трюк: Array.sort()
может принимать функцию в качестве аргумента.
keywords = keywords.sort((a, b) => { if (a < b) return -1; if (a > b) return 1; return 0; });
Это можно сократить с помощью «оператора Элвиса»:
keywords = keywords.sort((a, b) => { return (a < b) ? -1 : 1; });
Или, если совсем коротко:
keywords = keywords.sort((a, b) => a < b ? -1 : 1);
Но что, если мы хотим упорядочить наш массив по обоим полям? То есть не только отсортировать по word
, но и сгруппировать результаты по occurrence
, чтобы ключевые слова с наибольшим количеством вхождений оставались вверху нашего списка?
Вот как.
keywords = keywords.sort((a, b) => { let retval = 0; if (a.occurrences > b.occurrences) retval = -1; if (a.occurrences < b.occurrences) retval = 1; if (retval === 0) retval = a.keyword < b.keyword ? -1 : 1; return retval; });
Что тут происходит?
Функция, переданная Array.sort()
, определяет, как два отдельных элемента массива должны сравниваться друг с другом. Любые два элемента, проходящие через функцию, при этом названные довольно нетворчески a
и b
, будут сравниваться, и если a
должно стоять перед b
, функция возвращает -1, если после него 1. 0 означает, что сортировки не будет, поэтому порядок элементов принимается таким, какой он есть. Array.sort()
выполнит остальную работу и пройдется по нашему массиву, чтобы выполнить сравнение для каждого его элемента. То, что мы здесь делаем, в значительной степени является скриптом внутри скрипта.
Когда функция запускается, она создает возвращаемую переменную со значением 0.
Затем он проверяет, больше или меньше a.occurrences
, чем b.occurrences
. Строка с melon
будет предшествовать apple
, потому что первая имеет значение 2, а вторая 1.
Но если две строки имеют одинаковое количество occurrences
, возвращаемое значение останется равным 0. В этом случае они будут отсортированы по word
в алфавитном порядке.
Это будет наш результат:
let keywords = [ { word: 'pear', occurrences: 4 }, { word: 'melon', occurrences: 2 }, { word: 'orange', occurrences: 2}, { word: 'apple', occurrences: 1 }, { word: 'plum', occurrences: 1 } ];
Вот в чем хитрость: сделайте сравнение на основе одного поля. Если между элементами в этом поле нет разницы, поэтому возвращаемое значение равно 0, повторите это со следующим полем (если оно есть, поскольку в этом примере их только два). Наконец, если возвращаемое значение по-прежнему равно 0 после последнего, выполните окончательное сравнение.