145 votes

Comment trier un tableau sur plusieurs colonnes ?

J'ai un tableau multidimensionnel. Le tableau primaire est un tableau de

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

Ce que j'essaie de faire, c'est de trier le tableau par owner_name et ensuite par publication_name . Je sais qu'en JavaScript vous avez Array.sort() dans lequel vous pouvez placer 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));
}

Cela convient pour trier sur une seule colonne, à savoir owner_name, mais comment puis-je le modifier pour trier sur owner_name entonces publication_name ?

2voto

WebWanderer Points 324

Je travaillais avec ng-grid et j'avais besoin de trier plusieurs colonnes sur un tableau d'enregistrements renvoyés par une API, j'ai donc créé cette fonction multi-tri dynamique très pratique.

Tout d'abord, ng-grid déclenche un "événement" pour "ngGridSorted" et renvoie cette structure décrivant le tri :

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

J'ai donc créé une fonction qui génère dynamiquement une fonction de tri sur la base de l'élément sortData comme indiqué ci-dessus ( Ne vous laissez pas effrayer par la barre de défilement ! Il ne s'agit que d'une cinquantaine de lignes ! Par ailleurs, je suis désolé pour la pente. Cela a empêché l'apparition d'une 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 trie ensuite les résultats de mon API ( results ) comme suit :

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

J'espère que quelqu'un d'autre appréciera cette solution autant que moi ! Merci à tous !

2voto

Hefeust Points 97

J'ai eu un problème similaire en affichant les blocs du pool de mémoire à partir de la sortie de la composition de certaines fonctions virtuelles du DOM. En fait, j'ai été confronté au même problème en triant des données multicritères telles que les résultats des joueurs du monde entier.

J'ai remarqué que le tri multicritères est :

- sort by the first column
- if equal, sort by the second
- if equal, sort by the third
-  etc... nesting and nesting if-else

Et si vous ne vous en souciez pas, vous risquez d'échouer rapidement dans un enfer d'imbrication if-else... comme l'enfer du callback des promesses...

Et si nous écrivions une fonction "prédicat" pour décider de la partie de l'alternative à utiliser ? Le prédicat est simplement :

// useful for chaining test
const decide = (test, other) => test === 0 ? other : test

Maintenant, après avoir écrit vos tests de classification (byCountrySize, byAge, byGameType, byScore, byLevel...) selon vos besoins, vous pouvez pondérer vos tests (1 = asc, -1 = desc, 0 = disable), les mettre dans un tableau, et appliquer une fonction réductrice 'decide' comme ceci :

const multisort = (s1, s2) => {
  const bcs = -1 * byCountrySize(s1, s2) // -1 = desc 
  const ba =  1 *byAge(s1, s2)
  const bgt = 0 * byGameType(s1, s2) // 0 = doesn't matter
  const bs = 1 * byScore(s1, s2)
  const bl = -1 * byLevel(s1, s2) // -1 = desc

  // ... other weights and criterias

  // array order matters !
  return [bcs, ba, bgt, bs, bl].reduce((acc, val) => decide(val, acc), 0)
}

// invoke [].sort with custom sort...
scores.sort(multisort)

Et voilà ! A vous de définir vos propres critères / poids / ordres... mais vous voyez l'idée. J'espère que cela vous aidera !

E * s'assurer qu'il y a un ordre de tri total sur chaque colonne * s'assurer qu'il n'y a pas de dépendances entre les ordres des colonnes, et qu'il n'y a pas de dépendances circulaires

sinon, le tri peut être instable !

1voto

Lewiss Points 17
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'

1voto

spender Points 51307

Ma propre bibliothèque pour travailler avec les itérables ES6 (blinq) permet (entre autres) un tri facile à plusieurs niveaux

const blinq = window.blinq.blinq
// or import { blinq } from 'blinq'
// or const { blinq } = require('blinq')
const dates = [{
    day: 1, month: 10, year: 2000
  },
  {
    day: 1, month: 1, year: 2000
  },
  {
    day: 2, month: 1, year: 2000
  },
  {
    day: 1, month: 1, year: 1999
  },
  {
    day: 1, month: 1, year: 2000
  }
]
const sortedDates = blinq(dates)
  .orderBy(x => x.year)
  .thenBy(x => x.month)
  .thenBy(x => x.day);

console.log(sortedDates.toArray())
// or console.log([...sortedDates])

<script src="https://cdn.jsdelivr.net/npm/blinq@2.0.2"></script>

1voto

Romain Deneau Points 788

Je viens de publier sur npm une micro-bibliothèque appelée aide au tri ( source sur github ) . L'idée est d'importer l'aide by pour créer la fonction de comparaison pour sort par le biais de la syntaxe items.sort(by(column, ...otherColumns)) avec plusieurs façons d'exprimer les colonnes à trier :

  • Par clé : persons.sort(by('lastName', 'firstName')) ,
  • Par sélecteur : dates.sort(by(x => x.toISOString())) ,
  • En ordre décroissant : [3, 2, 4, 1].sort(by(desc(n => n)))[3, 2, 1, 0] ,
  • Ignorer l'affaire : ['B', 'D', 'c', 'a'].sort(by(ignoreCase(x => x))).join('')'aBcD' .

Il s'agit d'un système similaire à l'agréable thenBy mentionné dans cette réponse mais avec les différences suivantes qui peuvent être plus au goût de certains :

  • Une approche plus fonctionnelle qu'orientée objet (voir thenBy fluent API) ,
  • Une syntaxe un peu plus serrée et toujours aussi lisible, naturelle presque comme SQL.
  • Entièrement implémenté en TypeScript, pour bénéficier de la sécurité et de l'expressivité des types.

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