Javascript trie le tableau par deux champs

88
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);      
    return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});

Ainsi, le code ci-dessus trie le tableau par gsize - du plus petit au plus grand. Ça marche bien. Mais si le gsize est le même, je voudrais qu'il trie ensuite par lueur.

Merci.

marque
la source
La fonction de tri réagit sur un résultat positif, négatif ou nul. vous pouvez donc simplement écrire: "return aSize - bSize". ce sera un code plus simple et plus lisible.

Réponses:

107
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);

    if(aSize == bSize)
    {
        return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
    }
    else
    {
        return (aSize < bSize) ? -1 : 1;
    }
});
Chris Eberle
la source
170
grouperArray.sort(function (a, b) {   
    return a.gsize - b.gsize || a.glow - b.glow;
});

version plus courte

anmorozov23
la source
super raccourci! m'a aidé à mettre en place une solution plus complexe .. stackoverflow.com/questions/6101475/…
Joseph Poirier
3
Agréable et propre! La seule chose que cela ne fonctionne que pour les nombres.
Afanasii Kurakin
Pouvez-vous expliquer la logique ici?!. Cela a fonctionné pour moi sort an array with a key's value firstet puissort the result with another key's value
KTM
1
@KTM La logique est la suivante: si les deux gsize sont égaux, alors la première partie de la condition est égale à 0, ce qui est considéré comme faux, et la seconde partie de la condition est exécutée.
Scalpweb
@Scalpweb Ouais :) donc cela fonctionne pour trier un tableau avec n'importe quel nombre de clés une par une, non?! Belle astuce
KTM
35
grouperArray.sort((a, b) => a.gsize - b.gsize || a.glow - b.glow);

Version encore plus courte utilisant la syntaxe des flèches!

Vinorth
la source
3
Le plus concis et extensible, parfait!
Laurent
1
Encore plus court, sortez les espaces:grouperArray.sort((a,b)=>a.gsize-b.gsize||a.glow-b.glow);
Captain Fantastic
si vous voulez comprendre pourquoi cela fonctionne, vous voudrez peut-être regarder medium.com/@safareli/pss-ordering-is-a-monoid-61a4029387e
Safareli
14

Je me rends compte que cela a été demandé il y a quelque temps, mais j'ai pensé ajouter ma solution.

Cette fonction génère des méthodes de tri dynamiquement. fournissez simplement chaque nom de propriété enfant triable, précédé de +/- pour indiquer l'ordre croissant ou décroissant. Super réutilisable, et il n'a pas besoin de savoir quoi que ce soit sur la structure de données que vous avez créée. Cela pourrait être une preuve idiote - mais cela ne semble pas nécessaire.

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;

            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;

            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

exemple d'utilisation:

items.sort (getSortMethod ('- prix', '+ priorité', '+ nom'));

ce serait le tri le itemsplus bas en pricepremier, les liens allant à l'élément avec le plus hautpriority . d'autres liens sont rompus par l'articlename

où items est un tableau comme:

var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];

démo en direct: http://gregtaff.com/misc/multi_field_sort/

EDIT: Correction d'un problème avec Chrome.

Nihlton
la source
C'est brillant
Azure
Réponse de génie!
Marius
pour dactylographié (pour ne pas obtenir un error TS2554: Expected 0 arguments, but got ..), utilisez la syntaxe ici: stackoverflow.com/a/4116634/5287221
Chananel P
6

Je suppose que l' opérateur ternaire((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) vous a confus. Vous devriez consulter le lien pour mieux le comprendre.

Jusque-là, voici votre code explosé si / else.

grouperArray.sort(function (a, b) {
    if (a.gsize < b.gsize)
    {
        return -1;
    }
    else if (a.gsize > b.gsize)
    {
        return 1;
    }
    else
    {
        if (a.glow < b.glow)
        {
            return -1;
        }
        else if (a.glow > b.glow)
        {
            return 1;
        }
        return 0;
    }
});
John Green
la source
6

Voici une implémentation pour ceux qui veulent quelque chose de plus générique qui fonctionnerait avec n'importe quel nombre de champs.

Array.prototype.sortBy = function (propertyName, sortDirection) {

    var sortArguments = arguments;
    this.sort(function (objA, objB) {

        var result = 0;
        for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {

            var propertyName = sortArguments[argIndex];
            result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;

            //Reverse if sort order is false (DESC)
            result *= !sortArguments[argIndex + 1] ? 1 : -1;
        }
        return result;
    });

}

Fondamentalement, vous pouvez spécifier n'importe quel nombre de nom de propriété / sens de tri:

var arr = [{
  LastName: "Doe",
  FirstName: "John",
  Age: 28
}, {
  LastName: "Doe",
  FirstName: "Jane",
  Age: 28
}, {
  LastName: "Foo",
  FirstName: "John",
  Age: 30
}];

arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo

arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe
The_Black_Smurf
la source
3
grouperArray.sort(function (a, b) {
  var aSize = a.gsize;
  var bSize = b.gsize;
  var aLow = a.glow;
  var bLow = b.glow;
  console.log(aLow + " | " + bLow);      
  return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});
silex
la source
3
grouperArray.sort(function (a, b) {
     var aSize = a.gsize;     
     var bSize = b.gsize;     
     var aLow = a.glow;
     var bLow = b.glow;
     console.log(aLow + " | " + bLow);
     return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); }); 
Tim Williams
la source
3

Voici une implémentation qui utilise la récursivité pour trier selon n'importe quel nombre de champs de tri de 1 à infini. Vous lui transmettez un tableau de résultats qui est un tableau d'objets de résultat à trier et un tableau de tris qui est un tableau d'objets de tri définissant le tri. Chaque objet de tri doit avoir une clé "sélection" pour le nom de clé par lequel il trie et une clé "ordre" qui est une chaîne indiquant "croissant" ou "décroissant".

sortMultiCompare = (a, b, sorts) => {
    let select = sorts[0].select
    let order = sorts[0].order
    if (a[select] < b[select]) {
        return order == 'ascending' ? -1 : 1
    } 
    if (a[select] > b[select]) {
        return order == 'ascending' ? 1 : -1
    }
    if(sorts.length > 1) {
        let remainingSorts = sorts.slice(1)
        return this.sortMultiCompare(a, b, remainingSorts)
    }
    return 0
}

sortResults = (results, sorts) => {
    return results.sort((a, b) => {
        return this.sortMultiCompare(a, b, sorts)
    })
}

// example inputs
const results = [
    {
        "LastName": "Doe",
        "FirstName": "John",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Doe",
        "FirstName": "Jane",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Johnson",
        "FirstName": "Kevin",
        "MiddleName": "Bill"
    }
]

const sorts = [
    {
        "select": "LastName",
        "order": "ascending"
    },
    {
        "select": "FirstName",
        "order": "ascending"
    },
    {
        "select": "MiddleName",
        "order": "ascending"
    }    
]

// call the function like this:
let sortedResults = sortResults(results, sorts)
Benjamin Portman
la source
2

Une façon dynamique de faire cela avec PLUSIEURS touches:

  • filtrer les valeurs uniques de chaque col / clé de tri
  • mettre en ordre ou inverser
  • ajouter des poids width zeropad pour chaque objet en fonction des valeurs des clés indexOf (value)
  • trier à l'aide de poids calculés

entrez la description de l'image ici

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

Utilisation:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

entrez la description de l'image ici

Léonard Filipe
la source
1

C'est ce que j'utilise

function sort(a, b) {
    var _a = "".concat(a.size, a.glow);
    var _b = "".concat(b.size, b.glow);
    return _a < _b;
}

concat les deux éléments sous forme de chaîne et ils seront triés par une valeur de chaîne. Si vous le souhaitez, vous pouvez envelopper _a et _b avec parseInt pour les comparer sous forme de nombres si vous savez qu'ils seront numériques.

blockloop
la source
1

Voici la solution pour le cas, lorsque vous avez une clé de tri prioritaire, qui peut ne pas exister dans certains éléments particuliers, vous devez donc trier par clés de secours.

Un exemple de données d'entrée ( id2 est la clé de tri prioritaire):

const arr = [
    {id: 1},
    {id: 2, id2: 3},
    {id: 4},
    {id: 3},
    {id: 10, id2: 2},
    {id: 7},
    {id: 6, id2: 1},
    {id: 5},
    {id: 9, id2: 2},
    {id: 8},
];

Et la sortie devrait être:

[ { id: 6, id2: 1 },
  { id: 9, id2: 2 },
  { id: 10, id2: 2 },
  { id: 2, id2: 3 },
  { id: 1 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 7 },
  { id: 8 } ]

La fonction comparateur sera comme:

arr.sort((a,b) => {
  if(a.id2 || b.id2) {
    if(a.id2 && b.id2) {
      if(a.id2 === b.id2) {
        return a.id - b.id;
      }
      return a.id2 - b.id2;
    }
    return a.id2 ? -1 : 1;
  }
  return a.id - b.id
});

PS Dans le cas où .id de .id2 peut être des zéros, pensez à utiliser typeof.

Deliaz
la source
0
grouperArray.sort(
  function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);
ic3b3rg
la source
0
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    if (aSize !== aSize)
        return aSize - bSize;
    return a.glow - b.glow;
});

pas testé, mais je pense que cela devrait fonctionner.

Alex
la source
0

Dans mon cas, je trie la liste des notifications par paramètre «important» et par «date»

  • étape 1: je filtre les notifications par `` important '' et non important

    let importantNotifications = notifications.filter(
            (notification) => notification.isImportant);
    
      let unImportantNotifications = notifications.filter(
            (notification) => !notification.isImportant);
    
  • étape 2: je les trie par date

      sortByDate = (notifications) => {
      return notifications.sort((notificationOne, notificationTwo) => {
        return notificationOne.date - notificationTwo.date;
      });
    };
    
  • étape 3: fusionnez-les

    [
        ...this.sortByDate(importantNotifications),
        ...this.sortByDate(unImportantNotifications),
      ];
    
Phạm Hùng
la source