web-dev-qa-db-fra.com

Comment utiliser lodash/underscore pour trier plusieurs champs imbriqués?

Je veux faire quelque chose comme ça:

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);});

Et le faire sortir ceci:

"a 1"
"a 2"
"a 3"
"b 5"

Malheureusement, cela ne fonctionne pas et le tableau reste trié dans sa forme d'origine. Cela fonctionnerait si les champs n'étaient pas imbriqués dans la sortData. Comment utiliser lodash/underscore pour trier un tableau d'objets selon plusieurs champs imbriqués?

J'ai transformé cela en une demande de fonctionnalité lodash: https://github.com/lodash/lodash/issues/581

26
Daniel Kaplan

Il existe une méthode _.sortByAll dans la version 3 de lodash:

https://github.com/lodash/lodash/blob/3.10.1/doc/README.md#_sortbyallcollection-iteratees

Lodash version 4, il a été unifié:

https://lodash.com/docs#sortBy

Une autre option serait de trier les valeurs vous-même:

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 }
// ];
17
Tomas Kirda

Mise à jour: Voir les commentaires ci-dessous, ce n'est pas une bonne solution dans la plupart des cas.


Quelqu'un a gentiment répondu dans le numéro que j'ai créé . Voici sa réponse, en ligne:

_.sortBy(data, function(item) {
   return [item.sortData.a, item.sortData.b];
});

Je n'avais pas réalisé que vous étiez autorisé à retourner un tableau de cette fonction. La documentation ne mentionne pas cela. 

33
Daniel Kaplan

Si vous devez spécifier le sens du tri, vous pouvez utiliser _.orderBy avec la syntaxe du tableau de fonctions de Lodash 4.x:

_.orderBy(data, [
  function (item) { return item.sortData.a; },
  function (item) { return item.sortData.b; }
], ["asc", "desc"]);

Cela triera d'abord en ordre croissant par la propriété a, et pour les objets ayant la même valeur pour la propriété a, les triera par ordre décroissant en propriété b.

Cela fonctionne comme prévu lorsque les propriétés a et b ont des types différents.

Voici un exemple jsbin en utilisant cette syntaxe.

17
kenjiru

Le moyen simple et génial est:

_.sortBy(data, [function(item) {
    return item.sortData.a;
}, function(item) {
    return item.sortData.b;
}]);

Je l'ai trouvé en vérifiant le code source de lodash, il vérifie toujours la fonction un par un.

J'espère que cette aide.

10
KimKha

Avec ES6 easy syntax et lodash

sortBy(item.sortData, (item) => (-item.a), (item) => (-item.b))
5
Cem Arguvanlı

Si le problème est qu'un entier est converti en chaîne, ajoutez des zéros avant l'entier pour lui donner la même longueur que le plus long de la collection:

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];
});
0
user1912899

Je pense que cela pourrait fonctionner dans la plupart des cas avec un trait de soulignement:

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)
});

Cela fonctionne et vous pouvez le voir à Plunker

0
David Martin