Как отсортировать данные разных типов по приоритету? - PullRequest
0 голосов
/ 22 января 2019

Я сортирую список объектов по переменной, хранящейся в них. Назовем эту переменную "sortCriteria"
Пример набора значений «sortCriteria» для разных объектов:

400, 329, 529, «String1», 678, «String2», 588, «String3», «String1», 201, «String2»

Итак, в «sortCriteria» я могу получить 4 вида значений:
1. Числовое значение
2. Строка1
3. Строка2
4. Строка3

Теперь я должен отсортировать эти данные таким образом, чтобы числовые данные имели самый приоритетный, затем «String1», затем «String2» и затем «String3». т.е.
Приоритет (Числовой> Строка1> Строка2> Строка3)
Обратите внимание, что при выводе все эти числовые значения должны быть в отсортированном порядке.

Следовательно, отсортированный порядок выборки данных будет - 201, 329, 400, 529, 588, 678, «String1», «String1», «String2», «String2», «String3».
Кроме того, если несколько объектов имеют одинаковые значения sortCriteria, их порядок следует сохранить.
Например.
Допустим, я получил 2 объекта, у которых значение sortCriteria одинаково Объект 1: 205, Объект 2: 205. Затем в отсортированном порядке Object1 должен предшествовать Object2.


Моя текущая реализация сортировки определенной логики на Javascript выглядит так:

function mySortRule(a, b) {
         var value1 = a[1], value2 = b[1];
         var value1Priority = getPriorityOf(value1);
         var value2Priority = getPriorityOf(value2);
         return value1Priority - value2Priority;
}
function getPriorityOf(value) {
         var priority;
         if(value!="String1" && value!="String2" && value!="String3") {
             priority = value;
         }
         else if(value == "String1") {
            priority = Number.MAX_VALUE-2;
         }
         else if(value == "String2") {
            priority = Number.MAX_VALUE-1;
         }
         else if(value == "String3") {
            priority = Number.MAX_VALUE;
         }
         return priority;
}
sortCriteriaArray.sort(mySortRule);

Значение sortCriteriaArray [i] имеет следующий формат: ["indexOfObject", "sortCriteria"]

Это решение работает, но не сохраняет порядок объектов. Кроме того, я не чувствую, что это хороший подход, потому что -
1. Завтра, допустим, мы должны вписаться в некоторые другие типы строк. В этом случае нам придется изменить эти условные операторы в функции getPriorityOf ().
2. Использование «Number.MAX_VALUE» для установки приоритета выглядит мне неприлично.
Может ли быть лучший способ добиться этого?

Ответы [ 4 ]

0 голосов
/ 26 апреля 2019
function mySortRule(a, b) {
    function getPriorityOf(value) {
        // priority order..
        return [Boolean, Number, String, Array, Object, Function]
            .reverse().indexOf(value.constructor);
    }
    // smallest first..
    return getPriorityOf(a) == getPriorityOf(b) ?
        (a.valueOf() == b.valueOf() ?
            0 :
            (a.valueOf() > b.valueOf() ? 1 : -1)) :
        getPriorityOf(b) - getPriorityOf(a)
}
var sortCriteriaArray = 
    [400, 329, 529, "String1", 678, "String2", 588, "String3", "String1", 201, "String2"];
sortCriteriaArray.sort(mySortRule);
// -> [201, 329, 400, 529, 588, 678, "String1", "String1", "String2", "String2", "String3"]

(Предполагается, что каждый элемент массива не является ни нулевым, ни неопределенным)

0 голосов
/ 22 января 2019

Сделать сравнительный тест лексикографическим, сначала по типу (присваивая приоритетные индексы, такие как число: 0, строка: 1), затем по значению в случае связей.Это чисто и расширяемо.

Чтобы сохранить порядок, вам нужно использовать стабильный алгоритм сортировки, такой как MergeSort.


В качестве альтернативы, если вы не планируете добавлять другие типывыполните следующие действия:

  • отсканируйте список и разбейте (стабильно) на подсписок с номерами и еще один подсписок со строками;

  • сортировать подсписки и конкатенировать их.

0 голосов
/ 22 января 2019

Учитывая, что у вас небольшое и фиксированное количество строк, вы можете построить составное правило сортировки, используя объект в качестве словаря для приоритетов вместо вашего getPriorityOf, и тогда вам не нужно будет использовать MAX_VALUE. Вы можете взять объект для порядка заданных строк или значения по умолчанию, равного нулю (при условии, что числа являются верхним приоритетом), а затем отсортировать по числовому значению.

var data = [400, 329, 529, "String1", 678, "String2", 588, "String3", "String1", 201, "String2"],
    order = { String1: 1, String2: 2, String3: 3 };

data.sort((a, b) => (order[a] || 0) - (order[b] || 0) || a - b);

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

К сожалению, Array.sort не гарантированно будет стабильным, хотя на практике это часто бывает. См. Также Об устойчивости алгоритма, используемого двигателем V8

Если она нестабильна в вашей среде, вы можете эмулировать стабильную сортировку, используя WeakMap для сохранения исходных позиций.

Тот факт, что вы запрашиваете стабильность, подразумевает, что у вас действительно есть некоторые объекты, которые вы хотите отсортировать по некоторому полю. Я предполагаю, что это поле называется data. Обратите внимание, что свойство index не используется алгоритмом, оно только в этом примере, чтобы доказать стабильность порядка.

var data = [{ index: 0, data: 400 }, { index: 1, data: 329 }, { index: 2, data: 529 }, { index: 3, data: "String1" }, { index: 4, data: 678 }, { index: 5, data: "String2" }, { index: 6, data: 588 }, { index: 7, data: "String3" }, { index: 8, data: "String1" }, { index: 9, data: 201 }, { index: 10, data: "String2" }],
    indices = new WeakMap(data.map((o, i) => [o, i])),
    order = { String1: 1, String2: 2, String3: 3 };

data.sort((a, b) => 
    (order[a.data] || 0) - (order[b.data] || 0) || // get priority ordering
    a.data - b.data ||                             // order by number
    indices.get(a) - indices.get(b)                // keep same values in order
);

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0 голосов
/ 22 января 2019

Посмотрите на мое решение и проверьте, подходит ли оно:

// Array
const arr = [400, 329, 529, "String1", 678, "String2", 588, "String3", "String1", 201, "String2"];

// Filter methods
const isNumber  = (item) => !isNaN(item);
const checkString  = (str) => (item) => item === str;

// Sort/Config method
const sortPriority = () => {
   return [
   ...arr.filter(isNumber).sort((a, b) => a - b),
   ...arr.filter(checkString("String1")),
   ...arr.filter(checkString("String2")),
   ...arr.filter(checkString("String3")),
   ]
}

console.log(sortPriority());
...