Трехсторонняя функция сравнения для массивов в Javascript
Были другие вопросы о том, как сравнивать массивы в JavaScript?, То, что я хочу знать, - это самый простой способ написать / использовать трехстороннюю функцию сравнения, подобную той, которая требуется для Array.sort(). Вот пример использования по умолчанию, который не работает хорошо:
> [ [4,5,10], [4,5,6], [4,1,2] ].sort() // no compare function, uses the default one
[ [ 4, 1, 2 ],
[ 4, 5, 10 ], // oops, string sorting makes 10 < 6
[ 4, 5, 6 ] ]
Вот что я придумал:
// return -1 if lhs is "less" than rhs, +1 if "greater", and 0 if equal
// if lhs and rhs have different lengths, only the shorter part will be considered
function compareArrays(lhs, rhs) {
for (var ii = 0; ii < lhs.length; ii++) {
if (lhs[ii] < rhs[ii]) {
return -1;
} else if (lhs[ii] > rhs[ii]) {
return 1;
}
}
return 0;
}
Что дает нам то, что мы хотим:
> [ [4,5,10], [4,5,6], [4,1,2] ].sort(compareArrays)
[ [ 4, 1, 2 ],
[ 4, 5, 6 ],
[ 4, 5, 10 ] ]
Есть ли что-то более похожее на одну строку, или я должен определить свою собственную функцию всякий раз, когда я хочу это сделать?
Поддержка старых браузеров не обязательна. Использование библиотек, таких как jQuery или Underscore - это нормально.
Один из способов взглянуть на это: "Первое ненулевое значение из стандартного трехстороннего сравнения, примененного к каждой паре элементов". Но даже тогда я не нашел подходящего варианта в существующих библиотеках.
3 ответа
Я бы пошел с общей функцией создания сравнения, которая будет использоваться функционально:
function compareArrays(compareItems) {
return function(a, b) {
for (var r, i=0, l=Math.min(a.length, b.length); i<l; i++)
if (0 != (r = compareItems(a[i], b[i])))
return r;
return a.length - b.length;
};
}
// Examples:
var compareNumberArrays = compareArray(function(a,b){ return a-b; }),
compareGenericArrays = compareArray(function(a,b){ return +(a>b)||-(b>a); });
Теперь вы можете использовать
[ [4,5,10], [4,5,6], [4,1,2], [4,5] ].sort(compareNumberArrays)
Есть ли что-то более похожее на одну строку, или я должен определить свою собственную функцию всякий раз, когда я хочу это сделать?
Сравнение массивов слишком сложно для однострочника, вы должны использовать вспомогательную функцию. Там нет встроенного, который вы можете использовать везде.
Вероятно, не самая короткая из возможных, но наименьшее количество строк, которые я могу найти, это:
function compareArrays(lhs, rhs) {
var result;
lhs.some(function(v, i) {
return (result = v - rhs[i]);
});
return result;
}
или менее разумно:
function compareArrays(lhs, rhs, r) {
lhs.some(function(v, i) {return (r = v - rhs[i])});
return r;
}
редактировать
Кажется, тебе не нужны цифры. Часть сравнения может быть любым отношением, которое вы хотите, например, для строк:
function compareArrays(lhs, rhs, r) {
lhs.some(function(v, i) {return (r = v < rhs[i]? -1 : v > rhs[i]? 1 : 0)});
return r;
}
[['a','b','c'],['a','c','d'],['a','b','d']].sort(compareArrays) // a,b,c,a,b,d,a,c,d
Но функция сравнения должна знать, что она получает, поэтому она не сортирует числа как строки или строки как числа (и так далее…).
Если вы знаете, что это всегда тройки чисел (массивы длины 3), вы можете сделать это:
function combineNum(array) {
return (array[0] * 100) + (array[1] * 10) + array[2];
}
function compareArrays(lhs, rhs) {
return combineNum(rhs) - combineNum(lhs);
}