77 votes

Est-il possible d'étendre la méthode array.sort() de javascript pour accepter un autre paramètre ?

J'essaie de trier un tableau d'objets. Je préférerais ne pas écrire une méthode de tri personnalisée pour chaque attribut.

Y a-t-il un moyen d'étendre le système intégré array.sort() pour accepter un paramètre supplémentaire, décrivant l'attribut à trier ? Par exemple,

array.sort(function(a, b, attr) { return a.attr - b.attr; }, 'name');

0 votes

Votre exemple n'a aucun sens, car vous n'utilisez pas réellement l'option attr vous obtenez simplement le paramètre attr des objets. Vous voulez probablement le changer en : array.sort(function(a, b, attr) { return a[attr] - b[attr]; }, 'name');

156voto

Dave Newton Points 93112

Écrivez un générateur de fonctions qui accepte un nom de propriété :

function propComparator(prop) {
    return function(a, b) {
        return a[prop] - b[prop];
    }
}

arr.sort(propComparator('name'));

Vous pouvez également enregistrer les trieurs pour une utilisation ultérieure, directement ou en tant que paramètres :

var compareNames = propComparator('name');
var compareFoos = propComparator('foo');
...
arr.sort(compareNames);
takesComparator(compareFoos);

Mise à jour pour ES6, et faire en sorte que cela fonctionne réellement avec différents types.

Notez que sort les tris en place, ce qui peut ou non être souhaitable.

const arr = [
  { name: 'John', age: 92 },
  { name: 'Dave', age: 42 },
  { name: 'Justin', age: 3 }
]

const propComparator = (propName) =>
  (a, b) => a[propName] == b[propName] ? 0 : a[propName] < b[propName] ? -1 : 1

arr.sort(propComparator('name'))
console.log("By name", arr)

arr.sort(propComparator('age'))
console.log("By age", arr)

10voto

Domenic Points 40761

C'est ce que vous cherchez ?

function sortByProperty(array, propertyName) {
    return array.sort(function (a, b) {
        return a[propertyName] - b[propertyName];
    });
}

var sortedByName = sortByProperty(myArray, "name");

0 votes

Je ne vois pas une grande différence entre les deux versions ; @DaveNewton, pouvez-vous expliquer votre commentaire ? Par ailleurs, merci à vous deux, les deux réponses semblent pouvoir fonctionner...

0 votes

@danwoods La différence est une question de communication : Je préfère l'approche plus fonctionnelle-/OOP qui fait du tableau l'acteur principal de la déclaration : arr.sort(how) pour moi, c'est plus communicatif que how(arr, moreHow) . De plus, les fonctions générées agissent sur tout ce qui possède des propriétés portant ce nom, au lieu d'envelopper une fonction spécifique à un tableau, ou une fonction sort -spécifique à la fonction, entité.

0 votes

L'un d'eux vous donne une fonction de comparaison (Daves), et l'autre utilise une fonction anonyme. Donc si vous voulez la fonction de comparaison, utilisez celle qui la donne.

5voto

Jan Turoň Points 6598

Utiliser des prototypes pour comparer correctement des chaînes de caractères et des nombres.

Array.prototype.sortAttr = function(attr,reverse) {
  var sorter = function(a,b) {
    var aa = a[attr];
    var bb = b[attr];
    if(aa+0==aa && bb+0==bb) return aa-bb; // numbers
    else return aa.localeCompare(bb); // strings
  }
  this.sort(function(a,b) {
    var result = sorter(a,b);
    if(reverse) result*= -1;
    return result;
  });
};

Exemple

var data = [
  {name: "Josh", age: 18},
  {name: "John", age: 17},
  {name: "Bob", age: 20},
  {name: 0, age: "error"}
];

data.sortAttr("name");
// data is now sorted by name

0 votes

Le fait qu'il y ait ou non des prototypes n'est pas le problème des chaînes de caractères par rapport aux nombres, c'est un problème de la fonction de tri fournie.

1 votes

Le code lève parfois l'exception "too much recursion". Il est conseillé de modifier le trieur pour utiliser localeCompare() au lieu de la comparaison définie par l'utilisateur.

1 votes

@d.popov Quel archéologue vous êtes ! Il y a 6 ans localeCompare était bogué, il est peut-être temps de le mettre à jour maintenant. Merci d'avoir bien compris, la récursion était horrible, j'étais si naïf à l'époque...

3voto

dekdev Points 1323

Est-il possible d'étendre la méthode intégrée array.sort() pour accepter un paramètre supplémentaire ?

Toutes les réponses ci-dessus sont bonnes, mais j'ai pensé ajouter quelques informations sur les fonctions partielles.

Pour plus d'informations, voir bind dans MDN et Fonction partielle ou John Resig - fonction partielle

Exemple du MDN :

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

//  Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

Voici un exemple tiré de Fermeture de Google

goog.partial = function(fn, var_args) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    // Prepend the bound arguments to the current arguments.
    var newArgs = Array.prototype.slice.call(arguments);
    newArgs.unshift.apply(newArgs, args);
    return fn.apply(this, newArgs);
  };
};

pour utiliser cette fonction

    var fn=goog.partial(numberCompare,sortField,sortDirection);
    myarray.sort (fn);

    var numberCompare = function (sortField,sortDirection,value1,value2){
      // sort code goes here
    }

2voto

stallingOne Points 1199

Au cas où quelqu'un aurait besoin d'un ordre croissant, voici la solution de DaveNewton avec une option inverse.

const sorton = (prop, asc=0) => {
    if(!asc) return (a, b) => a[prop] == b[prop] ? 0 : a[prop] < b[prop] ? -1 : 1
    else return (b, a) => a[prop] == b[prop] ? 0 : a[prop] < b[prop] ? -1 : 1
}

arr.sort(propComparator('age', 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