Подтвердить что ты не робот

Как я могу использовать 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

4b9b3361

Ответ 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];
});