121 votes

Underscore: sortBy () basé sur plusieurs attributs

J'essaie de trier un tableau avec des objets basés sur plusieurs attributs. Par exemple, si le premier attribut est identique entre deux objets, un deuxième attribut doit être utilisé pour comparer les deux objets. Par exemple, considérons le tableau suivant:

 var patients = [
             [{name: 'John', roomNumber: 1, bedNumber: 1}],
             [{name: 'Lisa', roomNumber: 1, bedNumber: 2}],
             [{name: 'Chris', roomNumber: 2, bedNumber: 1}],
             [{name: 'Omar', roomNumber: 3, bedNumber: 1}]
               ];
 

En les triant selon l'attribut roomNumber , j'utiliserais le code suivant:

 var sortedArray = _.sortBy(patients, function(patient) {
    return patient[0].roomNumber;
});
 

Cela fonctionne bien, mais comment puis-je procéder pour que «John» et «Lisa» soient triés correctement?

261voto

Rory MacLeod Points 4574

sortBy indique qu'il s'agit d'un algorithme de tri stable; vous devriez donc pouvoir commencer par trier votre deuxième propriété, puis trier à nouveau votre première propriété, comme ceci:

 var sortedArray = _(patients).chain().sortBy(function(patient) {
    return patient[0].name;
}).sortBy(function(patient) {
    return patient[1].roomNumber;
}).value();
 

Lorsque le second sortBy découvre que John et Lisa ont le même numéro de chambre, il les garde dans l'ordre dans lequel ils les ont trouvés, le premier sortBy réglé sur "Lisa, John".

53voto

Dan Tao Points 60518

Voici un hacky astuce que j'utilise parfois dans ces cas: combiner les propriétés de telle façon que le résultat sera sortable:

var sortedArray = _.sortBy(patients, function(patient) {
  return [patient[0].roomNumber, patient[0].name].join("_");
});

Cependant, comme je l'ai dit, c'est assez hacky. Pour ce faire correctement, vous auriez probablement souhaitez réellement utiliser le langage JavaScript de base sort méthode:

patients.sort(function(x, y) {
  var roomX = x[0].roomNumber;
  var roomY = y[0].roomNumber;
  if (roomX !== roomY) {
    return compare(roomX, roomY);
  }
  return compare(x[0].name, y[0].name);
});

// General comparison function for convenience
function compare(x, y) {
  if (x === y) {
    return 0;
  }
  return x > y ? 1 : -1;
}

Bien sûr, cela va trier votre réseau en place. Si vous voulez une triés copie (comme _.sortBy vous donnera), clone de la matrice d'abord:

function sortOutOfPlace(sequence, sorter) {
  var copy = _.clone(sequence);
  copy.sort(sorter);
  return copy;
}

Sortir de l'ennui, j'ai juste écrit une solution générale (à trier par n'importe quel nombre de touches) pour cela: avoir un coup d'oeil.

12voto

zobidafly Points 33

btw votre initialiseur pour les patients est un peu bizarre, n'est-ce pas? pourquoi ne pas initialiser cette variable comme celle-ci, comme un véritable tableau d'objets, et non pas comme un tableau de tableaux de simple objet, c'est peut-être faute de frappe de l'émission):

var patients = [
        {name: 'Omar', roomNumber: 3, bedNumber: 1},
        {name: 'John', roomNumber: 1, bedNumber: 1},
        {name: 'Chris', roomNumber: 2, bedNumber: 1},
        {name: 'Lisa', roomNumber: 1, bedNumber: 2},
        {name: 'Kiko', roomNumber: 1, bedNumber: 2}
        ];

J'ai trié la liste différemment et ajouter Kiko en Lisa lit, juste pour le fun et voir quels changements faudrait-il faire...

var sorted = _(patients).sortBy( 
                    function(patient){
                       return [patient.roomNumber, patient.bedNumber, patient.name];
                    });

inspecter triés et vous verrez ce

[
{bedNumber: 1, name: "John", roomNumber: 1}, 
{bedNumber: 2, name: "Kiko", roomNumber: 1}, 
{bedNumber: 2, name: "Lisa", roomNumber: 1}, 
{bedNumber: 1, name: "Chris", roomNumber: 2}, 
{bedNumber: 1, name: "Omar", roomNumber: 3}
]

donc, ma réponse est : utiliser un tableau dans votre fonction de rappel c'est assez semblable à Tao Dan's réponse, j'ai juste oublier de le rejoindre (peut-être parce que j'ai enlevé le tableau de tableaux de l'article unique :))

et un testload serait intéressant...

1voto

Mark Sherretta Points 5272

Vous pouvez concaténer les propriétés que vous souhaitez trier dans l'itérateur:

 return [patient[0].roomNumber,patient[0].name].join('|');
 

ou quelque chose d'équivalent.

REMARQUE: puisque vous convertissez l'attribut numérique roomNumber en chaîne, vous devez effectuer quelque chose si vous avez un nombre de chambres supérieur à 10. Sinon, 11 viendra avant 2. Vous pouvez ajouter des zéros au début, c’est-à-dire 01 au lieu de 1.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X