Как я могу использовать lodash/underscore для сортировки по нескольким вложенным полям?
Я хочу сделать что-то вроде этого:
var data = [
{
sortData: {a: 'a', b: 2}
},
{
sortData: {a: 'a', b: 1}
},
{
sortData: {a: 'b', b: 5}
},
{
sortData: {a: 'a', b: 3}
}
];
data = _.sortBy(data, ["sortData.a", "sortData.b"]);
_.map(data, function(element) {console.log(element.sortData.a + " " + element.sortData.b);});
И пусть это выведет это:
"a 1"
"a 2"
"a 3"
"b 5"
К сожалению, это не работает, и массив остается отсортированным в исходной форме. Это будет работать, если поля не были вложены внутри sortData
. Как я могу использовать lodash/underscore для сортировки массива объектов более чем одним вложенным полем?
Я превратил это в запрос функции lodash: https://github.com/lodash/lodash/issues/581
Ответы
Ответ 1
В версии lodash версии 3 есть метод _.sortByAll
:
https://github.com/lodash/lodash/blob/3.10.1/doc/README.md#_sortbyallcollection-iteratees
Lodash version 4, он был унифицирован:
https://lodash.com/docs#sortBy
Другим вариантом будет сортировка значений самостоятельно:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
function compareValues(v1, v2) {
return (v1 > v2)
? 1
: (v1 < v2 ? -1 : 0);
};
var data = [
{ a: 2, b: 1 },
{ a: 2, b: 2 },
{ a: 1, b: 3 }
];
data.sort(function (x, y) {
var result = compareValues(x.a, y.a);
return result === 0
? compareValues(x.b, y.b)
: result;
});
// data after sort:
// [
// { a: 1, b: 3 },
// { a: 2, b: 1 },
// { a: 2, b: 2 }
// ];
Ответ 2
Обновление: См. комментарии ниже, это не очень хорошее решение в большинстве случаев.
Кто-то любезно ответил в проблеме, которую я создал. Здесь его ответ, подчеркнутый:
_.sortBy(data, function(item) {
return [item.sortData.a, item.sortData.b];
});
Я не понимал, что вам разрешено возвращать массив из этой функции. В документации это не упоминается.
Ответ 3
Удивительный, простой способ:
_.sortBy(data, [function(item) {
return item.sortData.a;
}, function(item) {
return item.sortData.b;
}]);
Я нашел его из проверки исходного кода lodash, он всегда проверяет функцию по одному.
Надеюсь, что это поможет.
Ответ 4
Если вам нужно указать направление сортировки, вы можете использовать _.orderBy
с массивом синтаксиса функций от Lodash 4.x:
_.orderBy(data, [
function (item) { return item.sortData.a; },
function (item) { return item.sortData.b; }
], ["asc", "desc"]);
Это отсортирует сначала по возрастанию по свойству a
, а для объектов, имеющих одинаковое значение для свойства a
, сортирует их по убыванию с помощью свойства b
.
Он работает как ожидалось, когда свойства a
и b
имеют разные типы.
Вот этот jsbin example, используя этот синтаксис.
Ответ 5
С ES6 простой синтаксис и lodash
sortBy(item.sortData, (item) => (-item.a), (item) => (-item.b))
Ответ 6
Я думаю, что это может работать в большинстве случаев с подчеркиванием:
var properties = ["sortData.a", "sortData.b"];
data = _.sortBy(data, function (d) {
var predicate = '';
for (var i = 0; i < properties.length; i++)
{
predicate += (i == properties.length - 1
? 'd.' + properties[i]
: 'd.' + properties[i] + ' + ')
}
return eval(predicate)
});
Он работает, и вы можете увидеть его в Plunker
Ответ 7
Если проблема состоит в том, что целое число преобразуется в строку, добавьте нули перед целым числом, чтобы сделать его равной той самой длине в коллекции:
var maxLength = _.reduce(data, function(result, item) {
var bString = _.toString(item.sortData.b);
return result > bString.length ? result : bString.length;
}, 0);
_.sortBy(data, function(item) {
var bString = _.toString(item.sortData.b);
if(maxLength > bString.length) {
bString = [new Array(maxLength - bString.length + 1).join('0'), bString].join('');
}
return [item.sortData.a, bString];
});