2010-05-06 6 views
72

J'ai un tableau multidimensionnel. Le tableau principal est un tableau deComment trier un tableau sur plusieurs colonnes?

[publicationID][publication_name][ownderID][owner_name] 

Ce que je suis en train de faire est de trier le tableau par owner_name puis par publication_name. Je sais en JavaScript que vous avez Array.sort(), dans lequel vous pouvez mettre une fonction personnalisée, dans mon cas j'ai:

function mysortfunction(a, b) { 
    var x = a[3].toLowerCase(); 
    var y = b[3].toLowerCase(); 

    return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 
} 

Ceci est bien pour tout le tri d'une colonne, à savoir OWNER_NAME, mais comment puis-je modifier ce pour trier sur owner_name, puis publication_name?

Répondre

113

Si les noms de propriétaire diffèrent, triez-les à leur place. Sinon, utilisez le nom de publication de la condition de départage.

function mysortfunction(a, b) { 

    var o1 = a[3].toLowerCase(); 
    var o2 = b[3].toLowerCase(); 

    var p1 = a[1].toLowerCase(); 
    var p2 = b[1].toLowerCase(); 

    if (o1 < o2) return -1; 
    if (o1 > o2) return 1; 
    if (p1 < p2) return -1; 
    if (p1 > p2) return 1; 
    return 0; 
} 
+0

@dcp Je ne vois pas comment il pourrait aussi trier le second attribut. À moins que vous ne l'ayez bouclé autant que le nombre de colonnes sélectionnées. Ai-je raison? par exemple. [[A, 10], [J, 15], [A, 5], [J, 5]] => [[A, 10], [A, 5], [J, 15], [J, 5]] ' –

+2

@ user26409021 - Non, ce n'est pas correct. Il finirait par être [[A, 5], [A, 10], [J, 5], [J, 15]]. Il trie d'abord par le premier attribut, et si ceux-ci sont identiques, il les trie par le second attribut. Ainsi, dans votre exemple, A viendrait avant J. Dans le cas où A est le même pour deux éléments, alors il utiliserait le deuxième attribut. Donc, pour [A, 10], [A, 5], 5 vient avant 10 donc il finirait par [A, 5], [A, 10] pour la commande. La chose que vous pouvez manquer est que la fonction mysort est appelée plusieurs fois lorsque vous utilisez Array.sort jusqu'à ce que le tri soit terminé. – dcp

+1

@dcp bien, la partie manquante dans votre réponse est la boucle. Haha. Merci. Dans le mien je répète votre fonction autant que les colonnes sélectionnées –

16

Ceci est pratique pour les tris alpha de toutes tailles. Passez les index que vous souhaitez trier, dans l'ordre, en tant qu'arguments.

Array.prototype.deepSortAlpha= function(){ 
    var itm, L=arguments.length, order=arguments; 

    var alphaSort= function(a, b){ 
     a= a.toLowerCase(); 
     b= b.toLowerCase(); 
     if(a== b) return 0; 
     return a> b? 1:-1; 
    } 
    if(!L) return this.sort(alphaSort); 

    this.sort(function(a, b){ 
     var tem= 0, indx=0; 
     while(tem==0 && indx<L){ 
      itm=order[indx]; 
      tem= alphaSort(a[itm], b[itm]); 
      indx+=1;   
     } 
     return tem; 
    }); 
    return this; 
} 

var arr= [[ "Nilesh","Karmshil"], ["Pranjal","Deka"], ["Susants","Ghosh"], 
["Shiv","Shankar"], ["Javid","Ghosh"], ["Shaher","Banu"], ["Javid","Rashid"]]; 

arr.deepSortAlpha(1,0); 
+0

Puis-je savoir d'où avez-vous recueilli ces données? [["Nilesh", "Karmshil"], ["Pranjal", "Deka"], ["Susants", "Ghosh"], ["Shiv", " Shankar "], [" Javid "," Ghosh "], [" Shaher "," Banu "], [" Javid "," Rashid "]]; – defau1t

19

Came à travers un besoin de faire des tris de tableaux d'objets asc et desc mixés de style SQL par des clés.

solution de Kennebec ci-dessus m'a aidé à ceci:

Array.prototype.keySort = function(keys) { 

keys = keys || {}; 

// via 
// https://stackoverflow.com/questions/5223/length-of-javascript-object-ie-associative-array 
var obLen = function(obj) { 
    var size = 0, key; 
    for (key in obj) { 
     if (obj.hasOwnProperty(key)) 
      size++; 
    } 
    return size; 
}; 

// avoiding using Object.keys because I guess did it have IE8 issues? 
// else var obIx = function(obj, ix){ return Object.keys(obj)[ix]; } or 
// whatever 
var obIx = function(obj, ix) { 
    var size = 0, key; 
    for (key in obj) { 
     if (obj.hasOwnProperty(key)) { 
      if (size == ix) 
       return key; 
      size++; 
     } 
    } 
    return false; 
}; 

var keySort = function(a, b, d) { 
    d = d !== null ? d : 1; 
    // a = a.toLowerCase(); // this breaks numbers 
    // b = b.toLowerCase(); 
    if (a == b) 
     return 0; 
    return a > b ? 1 * d : -1 * d; 
}; 

var KL = obLen(keys); 

if (!KL) 
    return this.sort(keySort); 

for (var k in keys) { 
    // asc unless desc or skip 
    keys[k] = 
      keys[k] == 'desc' || keys[k] == -1 ? -1 
      : (keys[k] == 'skip' || keys[k] === 0 ? 0 
      : 1); 
} 

this.sort(function(a, b) { 
    var sorted = 0, ix = 0; 

    while (sorted === 0 && ix < KL) { 
     var k = obIx(keys, ix); 
     if (k) { 
      var dir = keys[k]; 
      sorted = keySort(a[k], b[k], dir); 
      ix++; 
     } 
    } 
    return sorted; 
}); 
return this; 
}; 

Exemple d'utilisation:

var obja = [ 
    {USER:"bob", SCORE:2000, TIME:32, AGE:16, COUNTRY:"US"}, 
    {USER:"jane", SCORE:4000, TIME:35, AGE:16, COUNTRY:"DE"}, 
    {USER:"tim", SCORE:1000, TIME:30, AGE:17, COUNTRY:"UK"}, 
    {USER:"mary", SCORE:1500, TIME:31, AGE:19, COUNTRY:"PL"}, 
    {USER:"joe", SCORE:2500, TIME:33, AGE:18, COUNTRY:"US"}, 
    {USER:"sally", SCORE:2000, TIME:30, AGE:16, COUNTRY:"CA"}, 
    {USER:"yuri", SCORE:3000, TIME:34, AGE:19, COUNTRY:"RU"}, 
    {USER:"anita", SCORE:2500, TIME:32, AGE:17, COUNTRY:"LV"}, 
    {USER:"mark", SCORE:2000, TIME:30, AGE:18, COUNTRY:"DE"}, 
    {USER:"amy", SCORE:1500, TIME:29, AGE:19, COUNTRY:"UK"} 
]; 

var sorto = { 
    SCORE:"desc",TIME:"asc", AGE:"asc" 
}; 

obja.keySort(sorto); 

donne le résultat suivant:

0: {  USER: jane;  SCORE: 4000; TIME: 35;  AGE: 16; COUNTRY: DE; } 
1: {  USER: yuri;  SCORE: 3000; TIME: 34;  AGE: 19; COUNTRY: RU; } 
2: {  USER: anita; SCORE: 2500; TIME: 32;  AGE: 17; COUNTRY: LV; } 
3: {  USER: joe;  SCORE: 2500; TIME: 33;  AGE: 18; COUNTRY: US; } 
4: {  USER: sally; SCORE: 2000; TIME: 30;  AGE: 16; COUNTRY: CA; } 
5: {  USER: mark;  SCORE: 2000; TIME: 30;  AGE: 18; COUNTRY: DE; } 
6: {  USER: bob;  SCORE: 2000; TIME: 32;  AGE: 16; COUNTRY: US; } 
7: {  USER: amy;  SCORE: 1500; TIME: 29;  AGE: 19; COUNTRY: UK; } 
8: {  USER: mary;  SCORE: 1500; TIME: 31;  AGE: 19; COUNTRY: PL; } 
9: {  USER: tim;  SCORE: 1000; TIME: 30;  AGE: 17; COUNTRY: UK; } 
keySort: { } 

(en utilisant une fonction d'impression à partir here)

here is a jsbin example.

modifier: cleaned up and posted as mksort.js on github.

42

Je pense que ce que vous cherchez est thenBy.js: https://github.com/Teun/thenBy.js

Il vous permet d'utiliser la norme Array.sort, mais avec un style firstBy().thenBy().thenBy().

An example can be seen here.

+0

Soyez prudent avec les performances sur des jeux de données volumineux. Chaque fois que 'thenBy' est appelé, tous les éléments du tableau sont à nouveau bouclés. –

+2

Ce n'est certainement pas le cas. Quand vous appelez thenBy(), il construit une nouvelle fonction qui encapsule la précédente.Au moment du tri, javascript ne sera pas strictement "boucler" les éléments, mais il _will_ appelera la fonction que vous lui passez plusieurs fois. Le nombre d'appels ne changera pas en utilisant thenBy. Pour certaines considérations de performance, lisez: https://github.com/Teun/thenBy.js#a-word-on-performance –

+1

Je vois, j'avais tort, merci de penser à la performance. Peut-être ajouter une note sur les considérations de la mémoire de la création de fermetures avec de nouvelles fonctions? –

6

Vous pouvez concaténer les 2 variables ensemble en une clé de tri et l'utiliser pour votre comparaison.

list.sort(function(a,b){ 
    var aCat = a.var1 + a.var2; 
    var bCat = b.var1 + b.var2; 
    return (aCat > bCat ? 1 : aCat < bCat ? -1 : 0); 
}); 
+0

@GustavoRodrigues peut-être parce que c'est trop fragile. Il n'arriverait pas à trier de la manière attendue sur certaines touches d'entrée, car il ne fait que fusionner les deux parties sans un délimiteur ou une autre distinction. Considérons si var1 et var2 pour l'item X étaient "foo" et "baz" alors que var1 pour l'item Y était "foobar". Quand trié X devrait venir en premier mais dans ce cas il serait deuxième. Cette réponse pourrait être améliorée, mais comme indiqué, ce n'est tout simplement pas sûr. –

0
function multiSort() { 

    var args =$.makeArray(arguments), 
     sortOrder=1, prop='', aa='', b=''; 

    return function (a, b) { 

     for (var i=0; i<args.length; i++){ 

     if(args[i][0]==='-'){ 
      prop=args[i].substr(1) 
      sortOrder=-1 
     } 
     else{sortOrder=1; prop=args[i]} 

     aa = a[prop].toLowerCase() 
     bb = b[prop].toLowerCase() 

     if (aa < bb) return -1 * sortOrder; 
     if (aa > bb) return 1 * sortOrder; 

     } 

     return 0 
    } 

} 
empArray.sort(multiSort('lastname','firstname')) Reverse with '-lastname' 
4

Je suggère d'utiliser un comparateur construit dans et de la chaîne de l'ordre de tri voulu avec logique ou ||.

function customSort(a, b) { 
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]); 
} 

Exemple de travail:

var array = [ 
 
    [0, 'Aluminium', 0, 'Francis'], 
 
    [1, 'Argon', 1, 'Ada'], 
 
    [2, 'Brom', 2, 'John'], 
 
    [3, 'Cadmium', 3, 'Marie'], 
 
    [4, 'Fluor', 3, 'Marie'], 
 
    [5, 'Gold', 1, 'Ada'], 
 
    [6, 'Kupfer', 4, 'Ines'], 
 
    [7, 'Krypton', 4, 'Joe'], 
 
    [8, 'Sauerstoff', 3, 'Marie'], 
 
    [9, 'Zink', 5, 'Max'] 
 
]; 
 

 
array.sort(function (a, b) { 
 
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]); 
 
}); 
 

 
document.write('<pre>'); 
 
array.forEach(function (a) { 
 
    document.write(JSON.stringify(a) + '<br>'); 
 
});

1

je travaillais avec ng-grid et besoin de la colonne multiple de tri sur un tableau d'enregistrements renvoyés d'une API, donc je suis venu avec cette fonction multi-tri astucieuse et dynamique.

Tout d'abord, ng-grid déclenche un « événement » pour « ngGridSorted » et passe cette structure arrière, décrivant le genre:

sortData = { 
    columns: DOM Element, 
    directions: [], //Array of string values desc or asc. Each index relating to the same index of fields 
    fields:  [], //Array of string values 
}; 

Je construit une fonction qui va générer dynamiquement une fonction de tri en fonction de la sortData comme indiqué ci-dessus (ne soyez pas effrayé par la barre de défilement Il est à seulement 50 lignes à long aussi, je suis désolé pour le slop Il a empêché la barre de défilement horizontale!.!):

function SortingFunction(sortData) 
{ 
    this.sortData = sortData; 

    this.sort = function(a, b) 
    { 
     var retval = 0; 

     if(this.sortData.fields.length) 
     { 
      var i = 0; 

      /* 
       Determine if there is a column that both entities (a and b) 
       have that are not exactly equal. The first one that we find 
       will be the column we sort on. If a valid column is not 
       located, then we will return 0 (equal). 
      */ 
      while( ( !a.hasOwnProperty(this.sortData.fields[i]) 
        || !b.hasOwnProperty(this.sortData.fields[i]) 
        || (a.hasOwnProperty(this.sortData.fields[i]) 
         && b.hasOwnProperty(this.sortData.fields[i]) 
         && a[this.sortData.fields[i]] === b[this.sortData.fields[i]]) 
        ) && i < this.sortData.fields.length){ 
       i++; 
      } 

      if(i < this.sortData.fields.length) 
      { 
       /* 
        A valid column was located for both entities 
        in the SortData. Now perform the sort. 
       */ 
       if(this.sortData.directions 
       && i < this.sortData.directions.length 
       && this.sortData.directions[i] === 'desc') 
       { 
        if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]]) 
         retval = -1; 
        else if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]]) 
         retval = 1; 
       } 
       else 
       { 
        if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]]) 
         retval = -1; 
        else if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]]) 
         retval = 1; 
       } 
      } 
     } 

     return retval; 
    }.bind(this); 
} 

Je puis trier les résultats de mon API (results) comme ceci:

results.sort(new SortingFunction(sortData).sort); 

J'espère que quelqu'un d'autre aime cette solution autant que moi! Merci!

+0

Quelle est l'option de colonnes sur les données de tri utilisées? –

7

Une bonne façon de trier sur de nombreux champs qui sont des chaînes est d'utiliser toLocaleCompare et l'opérateur booléen ||.

Quelque chose comme:

// Sorting record releases by name and then by title. 
releases.sort((oldRelease, newRelease) => { 
    const compareName = oldRelease.name.localeCompare(newRelease.name); 
    const compareTitle = oldRelease.title.localeCompare(newRelease.title); 

    return compareName || compareTitle; 
}) 

Si vous voulez trier sur plusieurs champs, vous pouvez simplement les chaîne de la déclaration de retour avec des opérateurs plus booléens.

+0

en fait, vous pouvez le ranger avec un '.reduce()' – ekkis

+0

Cependant, '.localCompare()' renvoie un -1, 0, 1 donc je ne pense pas que votre solution fonctionne comme le | | est bon pour les booléens – ekkis

+3

@ekkis, 1 et -1 sont "véridiques", donc c'est une solution très élégante. Je viens de faire ceci: 'sortItems = (a, b) => (a.distance - b.distance) || (a.name - b.name); ' et il fonctionne comme un charme pour mes besoins non pointilleux. – bstst

Questions connexes