Question Trier un tableau d'objets par valeur de propriété de chaîne en JavaScript


J'ai un tableau d'objets JavaScript:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

Comment puis-je les trier par la valeur de last_nom en JavaScript?

Je sais à propos de sort(a,b), mais cela ne semble fonctionner que sur les cordes et les nombres. Dois-je ajouter une méthode toString à mes objets?


1815
2017-07-15 03:17


origine


Réponses:


Il est assez facile d'écrire votre propre fonction de comparaison:

function compare(a,b) {
  if (a.last_nom < b.last_nom)
    return -1;
  if (a.last_nom > b.last_nom)
    return 1;
  return 0;
}

objs.sort(compare);

Ou en ligne (c / o Marco Demaio):

objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} ); 

2695
2017-07-15 03:35



Vous pouvez également créer une fonction de tri dynamique qui trie les objets selon leur valeur transmise:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

Vous pouvez donc avoir un tableau d'objets comme celui-ci:

var People = [
    {Name: "Name", Surname: "Surname"},
    {Name:"AAA", Surname:"ZZZ"},
    {Name: "Name", Surname: "AAA"}
];

... et cela fonctionnera quand vous faites:

People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));

En fait, cela répond déjà à la question. Ci-dessous la partie est écrite parce que beaucoup de gens m'ont contacté, se plaignant que cela ne fonctionne pas avec plusieurs paramètres.

Paramètres multiples

Vous pouvez utiliser la fonction ci-dessous pour générer des fonctions de tri avec plusieurs paramètres de tri.

function dynamicSortMultiple() {
    /*
     * save the arguments object as it will be overwritten
     * note that arguments object is an array-like object
     * consisting of the names of the properties to sort by
     */
    var props = arguments;
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length;
        /* try getting a different result from 0 (equal)
         * as long as we have extra properties to compare
         */
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i])(obj1, obj2);
            i++;
        }
        return result;
    }
}

Ce qui vous permettrait de faire quelque chose comme ça:

People.sort(dynamicSortMultiple("Name", "-Surname"));

L'ajouter au prototype

(La mise en œuvre qui est juste ci-dessous est inspirée de Mike Rde répondre)

Je ne recommanderais pas de changer un prototype d'objet natif, mais juste pour donner un exemple afin que vous puissiez l'implémenter sur vos propres objets (Pour les environnements qui le prennent en charge, vous pouvez également utiliser Object.defineProperty comme indiqué dans la section suivante, qui au moins n'a pas l'effet secondaire négatif d'être énumérable, comme décrit à la dernière partie)

La mise en œuvre du prototype serait quelque chose comme ce qui suit (Voici un exemple de travail):

//Don't just copy-paste this code. You will break the "for-in" loops
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Array.prototype.sortBy = function() {
        return this.sort(_dynamicSortMultiple.apply(null, arguments));
    }
}();

La façon "OK" de l'ajouter au prototype

Si vous ciblez IE v9.0 et supérieur, comme je l'ai mentionné précédemment, utilisez Object.defineProperty comme ça (exemple de travail):

//Won't work below IE9, but totally safe otherwise
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Object.defineProperty(Array.prototype, "sortBy", {
        enumerable: false,
        writable: true,
        value: function() {
            return this.sort(_dynamicSortMultiple.apply(null, arguments));
        }
    });
}();

Cela peut être un compromis acceptable jusqu'à ce que opérateur de liaison arrive.

Tous ces prototypes amusants permettent ceci:

People.sortBy("Name", "-Surname");

Vous devriez lire ceci

Si vous utilisez la méthode d'accès direct au prototype (Object.defineProperty est correct) et que le code ne vérifie pas hasOwnProperty, les chatons meurent! Ok, pour être honnête, aucun mal ne vient vraiment à un chaton mais probablement les choses vont se casser et tous les autres développeurs de votre équipe vous détesteront:

evil

Voir ce dernier "SortBy"? Ouais. Pas cool. Utilisez Object.defineProperty où vous le pouvez, et laissez le seul Array.prototype dans le cas contraire.


649
2018-01-21 15:03



underscore.js

utiliser le soulignement, sa petite et impressionnante ...

sortBy_.sortBy (list, itérateur, [contexte]) Retourne une copie triée de   liste, classés par ordre croissant selon les résultats de l'exécution de chaque valeur   à travers l'itérateur. Iterator peut également être le nom de la chaîne de la propriété   pour trier par (par exemple, la longueur).

var objs = [ 
  { first_nom: 'Lazslo',last_nom: 'Jamf' },
  { first_nom: 'Pig', last_nom: 'Bodine'  },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortedObjs = _.sortBy( objs, 'first_nom' );

155
2018-05-10 21:24



Ne comprends pas pourquoi les gens le rendent si compliqué:

objs.sort(function(a, b){
  return a.last_nom > b.last_nom;
});

Pour les moteurs plus stricts:

objs.sort(function(a, b){
  return a.last_nom == b.last_nom ? 0 : +(a.last_nom > b.last_nom) || -1;
});

Échangez l'opérateur pour qu'il soit trié par ordre alphabétique inverse.


140
2018-01-24 19:35



En ES6 / ES2015 ou plus tard, vous pouvez le faire de cette façon:

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));

136
2018-01-29 19:44



Si vous avez des noms de famille en double, vous pouvez les trier par prénom

obj.sort(function(a,b){
  if(a.last_nom< b.last_nom) return -1;
  if(a.last_nom >b.last_nom) return 1;
  if(a.first_nom< b.first_nom) return -1;
  if(a.first_nom >b.first_nom) return 1;
  return 0;
});

51
2017-07-15 04:03



Solution simple et rapide à ce problème en utilisant l'héritage du prototype:

Array.prototype.sortBy = function(p) {
  return this.slice(0).sort(function(a,b) {
    return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
  });
}

Exemple / Usage

objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];

objs.sortBy('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

objs.sortBy('name');
// Returns
// [{"age":24,"name":"deepak"},{"age":74,"name":"suresh"},{"age":44,"name":"vinay"}]

Mettre à jour: Ne modifie plus le tableau original.


38
2017-07-10 11:54



Au lieu d'utiliser une fonction de comparaison personnalisée, vous pouvez également créer un type d'objet personnalisé toString() méthode (qui est invoquée par la fonction de comparaison par défaut):

function Person(firstName, lastName) {
    this.firtName = firstName;
    this.lastName = lastName;
}

Person.prototype.toString = function() {
    return this.lastName + ', ' + this.firstName;
}

var persons = [ new Person('Lazslo', 'Jamf'), ...]
persons.sort();

23
2017-07-15 07:21



Il y a beaucoup de bonnes réponses ici, mais je voudrais souligner qu'elles peuvent être étendues très simplement pour réaliser un tri beaucoup plus complexe. La seule chose que vous devez faire est d'utiliser l'opérateur OU pour chaîner des fonctions de comparaison comme ceci:

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

fn1, fn2, ... sont les fonctions de tri qui retournent [-1,0,1]. Cela se traduit par "trier par fn1", "trier par fn2" qui est à peu près égal à ORDER BY dans SQL.

Cette solution est basée sur le comportement de || opérateur qui évalue à la première expression évaluée qui peut être convertie en vrai.

La forme la plus simple a seulement une fonction en ligne comme ceci:

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

Avoir deux étapes avec last_nom,first_nom L'ordre de tri ressemblerait à ceci:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || 
                  a.first_nom.localeCompare(b.first_nom)  )

Une fonction de comparaison générique pourrait être quelque chose comme ça:

// ORDER BY <n>
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

Cette fonction pourrait être étendue pour prendre en charge les champs numériques, la sensibilité à la casse, les types de données arbitraires, etc.

Vous pouvez les utiliser en les chaînant par ordre de tri:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

Le point ici est que le JavaScript pur avec l'approche fonctionnelle peut vous prendre un long chemin sans bibliothèques externes ou code complexe. Il est également très efficace, car aucune analyse de chaîne ne doit être effectuée


14
2018-05-05 11:36



Exemple d'utilisation:

objs.sort(sortBy('last_nom'));

Scénario:

/**
 * @description 
 * Returns a function which will sort an
 * array of objects by the given key.
 * 
 * @param  {String}  key
 * @param  {Boolean} reverse
 * @return {Function}     
 */
function sortBy(key, reverse) {

  // Move smaller items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  var moveSmaller = reverse ? 1 : -1;

  // Move larger items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  var moveLarger = reverse ? -1 : 1;

  /**
   * @param  {*} a
   * @param  {*} b
   * @return {Number}
   */
  return function(a, b) {
    if (a[key] < b[key]) {
      return moveSmaller;
    }
    if (a[key] > b[key]) {
      return moveLarger;
    }
    return 0;
  };

}

13
2018-04-30 10:02