J'ai un tableau multidimensionnel. Le tableau principal est un tableau de
[publicationID][publication_name][ownderID][owner_name]
Ce que j'essaie de faire est de trier le tableau par owner_name
Puis par publication_name
. Je sais qu'en JavaScript, vous avez Array.sort()
, dans laquelle vous pouvez insérer 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 très bien pour le tri sur une colonne, à savoir propriétaire_nom, mais comment puis-je le modifier pour trier sur owner_name
, Puis publication_name
?
Si les noms de propriétaires diffèrent, triez-les. Sinon, utilisez le nom de publication pour le 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;
}
Je pense que ce que vous cherchez, c'est thenBy.js: https://github.com/Teun/thenBy.js
Cela vous permet d'utiliser le standard Array.sort, mais avec le style firstBy().thenBy().thenBy()
.
Nous sommes tombés sur un besoin de faire des tris de tableaux d'objets asc et desc mélangés de type SQL.
la solution ci-dessus de Kennebec m'a aidé à atteindre ce point:
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;
};
utilisation de l'échantillon:
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 ce qui suit:
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 de ici )
Un bon moyen de trier 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 souhaitez effectuer un tri sur plusieurs champs, vous pouvez simplement les chaîner à partir de l'instruction de retour avec davantage d'opérateurs booléens.
C'est pratique pour les tris alpha de toutes les tailles. Transmettez-lui les index que vous voulez trier, dans l'ordre, comme 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);
Vous pouvez concatter les deux variables ensemble dans 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);
});
Je suggère d'utiliser un comparateur intégré et de chaîner l'ordre de tri souhaité avec la 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>');
});
J'ai trouvé multisotr . C'est une bibliothèque simple, puissante et petite pour un tri multiple. J'avais besoin de trier un tableau d'objets avec des critères de tri dynamiques:
const criteria = ['name', 'speciality']
const data = [
{ name: 'Mike', speciality: 'JS', age: 22 },
{ name: 'Tom', speciality: 'Java', age: 30 },
{ name: 'Mike', speciality: 'PHP', age: 40 },
{ name: 'Abby', speciality: 'Design', age: 20 },
]
const sorted = multisort(data, criteria)
console.log(sorted)
<script src="https://cdn.rawgit.com/peterkhayes/multisort/master/multisort.js"></script>
Cette bibliothèque plus puissante, c'était mon cas. Essayez-le.
Je travaillais avec ng-grid
et que plusieurs colonnes devaient être triées sur un tableau d'enregistrements renvoyés par une API, j'ai donc conçu cette fonction astucieuse et dynamique de tri multiple.
Tout d'abord, ng-grid
déclenche un "événement" pour "ngGridSorted" et renvoie cette structure, en 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 construit une fonction qui va générer dynamiquement une fonction de tri basée sur le sortData
comme indiqué ci-dessus (Ne soyez pas effrayé par la barre de défilement! Cela ne fait qu'environ 50 lignes! Aussi, je ' m désolé pour le slop, il a empêché 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écie cette solution autant que moi! Merci!
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'
Essaye ça:
t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );
let t = [
//[publicationID, publication_name, ownderID, owner_name ]
[1, 'ZBC', 3, 'John Smith'],
[2, 'FBC', 5, 'Mike Tyson'],
[3, 'ABC', 7, 'Donald Duck'],
[4, 'DBC', 1, 'Michael Jackson'],
[5, 'XYZ', 2, 'Michael Jackson'],
[6, 'BBC', 4, 'Michael Jackson'],
];
// owner_name subarrray index = 3
// publication_name subarrray index = 1
t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );
console.log(t.join('\n'));
Je suppose que vos données dans le tableau let t = [ [publicationID, publication_name, ownderID, owner_name ], ... ]
où index de nom de propriétaire = 3 et nom de publication = 1.
J'ai eu un problème similaire lors de l'affichage des blocs de pool de mémoire à partir de la sortie de certaines compositions de fonctions h du DOM virtuel. Fondamentalement, je rencontrais le même problème que le tri de données multicritères, comme l’obtention des résultats des joueurs du monde entier.
J'ai remarqué que le tri multicritère 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 pourriez échouer rapidement dans un enfer si-autrement imbriqué ... comme un enfer de rappel des promesses ...
Qu'en est-il si nous écrivons une fonction "prédicat" pour décider si quelle partie de l'utilisation alternative? 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 classement (byCountrySize, byAge, byGameType, byScore, byLevel ...), quels que soient vos besoins, vous pouvez pondérer vos tests (1 = asc, -1 = desc, 0 = désactiver), placez-les dans un tableau , et applique une fonction de réduction "décider" 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 le tour est joué! C'est à vous de définir vos propres critères/poids/commandes ... mais vous en avez l'idée. J'espère que cela t'aides !
EDIT: * assurez-vous qu'il y a un ordre de tri total sur chaque colonne * soyez conscient de l'absence de dépendances entre les ordres de colonnes et pas de dépendances circulaires
sinon, le tri peut être instable!
Ma propre bibliothèque pour utiliser ES6 iterables (blinq) permet (entre autres choses) de trier facilement sur 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/[email protected]"></script>