简体   繁体   中英

Sort an array by arbritrary object values (not ascending oder descending)

Hello this is a rather complicated question.

I have an array of objects:

let animals = [
{
    "typ": "rats",
    "name": "RB 1",
},
{
    "typ": "mice",
    "name": "MB 1",
    
},
{
    "typ": "rats",
    "name": "RB 4",
    
},
{
    "typ": "rats",
    "name": "RB 2",
    
},
{
    "typ": "rats",
    "name": "RB 3",
},
{
    "typ": "mice",
    "name": "MB 2",

},
{
    "typ": "mice",
    "name": "MB 3",
}

this array can have up to a few hundred objects and several other animals too!!!

now i want to sort it so that i have all rats first and then all mice after that...

A second step would be to sort all rats and mice by name like RB 1 , RB2 , RB 3 ....so that i get an array like this

[
{
    "typ": "rats",
    "name": "RB 1",
},
{
    "typ": "rats",
    "name": "RB 2",
    
},
{
    "typ": "rats",
    "name": "RB 3",
},
{
    "typ": "rats",
    "name": "RB 4",
    
},
{
    "typ": "mice",
    "name": "MB 1",
    
},
{
    "typ": "mice",
    "name": "MB 2",

},
{
    "typ": "mice",
    "name": "MB 3",
}]

I get the initial array from a firestore collection with snapshotChanges() so the objects(documents in the firestore database) all come in in random order

Thanx everybody

edit:

@secan posted a working solution! thanx man

 let animals = [{ "typ": "rats", "name": "RB 1", }, { "typ": "mice", "name": "MB 1", }, { "typ": "rats", "name": "RB 4", }, { "typ": "rats", "name": "RB 2", }, { "typ": "rats", "name": "RB 3", }, { "typ": "mice", "name": "MB 2", }, { "typ": "mice", "name": "MB 3", }, { "typ": "monkeys", "name": "MoB 2", }, { "typ": "monkeys", "name": "MoB 1", } ]; let animalsPriority = ['monkeys', 'rats', 'mice']; const sortedAnimals = animals.sort((a, b) => { return ( (animalsPriority.indexOf(a.typ) < animalsPriority.indexOf(b.typ) && -1) || (animalsPriority.indexOf(a.typ) > animalsPriority.indexOf(b.typ) && 1) || (a.name < b.name && -1) || (a.name > b.name && 1) || 0 ); }); console.log(sortedAnimals);

I wrote a small helper package that can be useful for multi-level sort: https://www.npmjs.com/package/ts-comparer-builder

Here's how you might use it:

 const animals = [{ "typ": "rats", "name": "RB 1", }, { "typ": "mice", "name": "MB 1", }, { "typ": "mice", "name": "MB 3", } ] const { comparerBuilder } = window.tsComparerBuilder const comparer = comparerBuilder() .sortKey(x => x.typ) .thenKeyDescending(x => x.name) .build(); const sortedAnimals = animals.sort(comparer); console.log(sortedAnimals);
 <script src="https://cdn.jsdelivr.net/npm/ts-comparer-builder"></script>

This approach takes into account one of the OP's requirements ...

this array can have up to a few hundred objects and several other animals too!!!

... and therefore it transforms a precedence list into a precedence map right before the sorting/comparison task. Twice a lookup via an object key will be much faster than computing an animal's precedence via indexOf for each comparison step 4 times again and again .

The approach also makes use of localeCompare .

 function localeCompare(a, b) { return (a.localeCompare && b.localeCompare) && a.localeCompare(b) || (((a < b) && -1) || ((a > b) && 1) || 0); } function compareAnimalsViaBoundPrecedenceMap(a, b) { const precedenceMap = this; return ( (precedenceMap[a.type] - precedenceMap[b.type]) || localeCompare(a.name, b.name) ); } function sortAnimalsViaPrecedenceList(animalList, precedenceList) { // build a map for a faster precedence lookup const precedenceMap = precedenceList.reduce((map, key, idx) => { map[key] = idx; return map; }, {}); // does return the mutated `animalList`. return animalList.sort( compareAnimalsViaBoundPrecedenceMap.bind(precedenceMap) ); } const animalList = [{ "type": "rats", "name": "RB 1", }, { "type": "mice", "name": "MB 1", }, { "type": "rats", "name": "RB 4", }, { "type": "rats", "name": "RB 2", }, { "type": "rats", "name": "RB 3", }, { "type": "mice", "name": "MB 2", }, { "type": "mice", "name": "MB 3", }]; console.log( 'sortAnimalsViaPrecedenceList(animalList, ["rats", "mice"]) :', sortAnimalsViaPrecedenceList(animalList, ["rats", "mice"]) ); const petList = [ {'type': 'rats', 'name': 'James'}, {'type': 'mice', 'name': 'John'}, {'type': 'guinea pigs', 'name': 'Robert'}, {'type': 'parrots', 'name': 'Michael'}, {'type': 'rats', 'name': 'William'}, {'type': 'mice', 'name': 'David'}, {'type': 'guinea pigs', 'name': 'Richard'}, {'type': 'parrots', 'name': 'Charles'}, {'type': 'rats', 'name': 'Joseph'}, {'type': 'mice', 'name': 'Thomas'}, {'type': 'guinea pigs', 'name': 'Andrew'}, {'type': 'parrots', 'name': 'George'} ]; console.log( 'sortAnimalsViaPrecedenceList(petList, ["rats", "mice", "guinea pigs", "parrots"]) :', sortAnimalsViaPrecedenceList(petList, ["rats", "mice", "guinea pigs", "parrots"]) );
 .as-console-wrapper { min-height: 100%!important; top: 0; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM