Kirjautuminen

Haku

Tehtävät

Koodit: JavaScript: Taulukon järjestäminen eri ehdoilla

Kirjoittaja: Metabolix

Kirjoitettu: 14.12.2020 – 14.12.2020

Tagit: ohjelmointitavat, hyvää koodia, vinkki

JavaScriptissa taulukon sort-funktio oletusarvoisesti järjestää taulukon tekstimuodossa. Niinpä esimerkiksi luvut 1, 9, 9 ja 10 päätyvät aakkosjärjestykseen 1, 10, 9, 9.

Järjestäminen muulla tavalla vaatii, että sort-funktiolle annetaan parametrina funktio, joka selvittää kahden asian toivotun järjestyksen. Funktion paluuarvona pitää olla negatiivinen luku, nolla tai positiivinen luku sen mukaan, onko ensimmäinen arvo pienempi, sama vai suurempi.

Kun järjestetään tavallisia lukuja numerojärjestykseen, vertailufunktio voi vähentää luvut toisistaan: 1 - 10 < 0, joten ykkönen tulee ensin. 10 - 9 > 0, joten kymppi tulee myöhemmin. 9 - 9 == 0, joten kaksi yhdeksikköä olisivat arvoltaan samat.

[1, 10, 9, 9].sort((a, b) => a - b);  // Tulos: [1, 9, 9, 10]

Oheinen sortNumbers-funktio toteuttaa tällaisen vertailun. Toisena parametrina voi antaa funktion, jolla taulukon kohdasta haetaan vertailtava luku. Oletuksena vain muutetaan taulukon kohdat luvuiksi vertailua varten. Toisaalta funktio voi esimerkiksi hakea oliosta tietyn arvon vertailtavaksi.

function sortNumbers(array, key) {
	key = key || (x => +x);
	return array.sort((a, b) => key(a) - key(b));
}
sortNumbers([ 1, 11, "2", "22", 5 ]);
// => [ 1, "2", 5, 11, "22" ]

sortNumbers(
  [
    {"nimi": "Mimi", "ikä": 10},
    {"nimi": "Pepe", "ikä": 13},
    {"nimi": "Xaxa", "ikä": 11},
    {"nimi": "Susu", "ikä": 9}
  ],
  x => x.ikä
);
/*
  [
    {"nimi": "Susu", "ikä": 9},
    {"nimi": "Mimi", "ikä": 10},
    {"nimi": "Xaxa", "ikä": 11},
    {"nimi": "Pepe", "ikä": 13}
  ]
*/

Tietysti vertailu voi olla myös monimutkaisempi, ja silloin pelkkä lukujen vähennyslasku ei käy, vaan vertailu täytyy tehdä vaihe kerrallaan. Esimerkiksi seuraavassa järjestetään henkilöt ensisijaisesti x-koordinaatin mukaan, sitten y-koordinaatin mukaan ja viime kädessä nimen mukaan.

[
  {"nimi": "Keke", "x": 1, "y": 1},
  {"nimi": "Pepe", "x": 1, "y": 0},
  {"nimi": "Xaxa", "x": 1, "y": 1},
  {"nimi": "Susu", "x": 0, "y": 1},
  {"nimi": "Mimi", "x": 0, "y": 0}
].sort((a, b) => {
  if (a.x != b.x) return a.x - b.x;
  if (a.y != b.y) return a.y - b.y;
  return a.nimi.localeCompare(b.nimi);
});
/*
[
  {"nimi": "Mimi", "x": 0, "y": 0},
  {"nimi": "Susu", "x": 0, "y": 1},
  {"nimi": "Pepe", "x": 1, "y": 0},
  {"nimi": "Keke", "x": 1, "y": 1},
  {"nimi": "Xaxa", "x": 1, "y": 1}
]
*/

Lisäys: Yllä käytetään nuolifunktiota paikan päällä, mutta vertailufunktio voi olla myös perinteinen nimetty funktio, kuten Grez alla kommentissa esittää.

Kommentit

Grez [14.12.2020 09:02:08]

#

Jos samaa vertailutapaa halutaan käyttää useammin, niin myös siitä voi tehdä funktion. Tämä on siis monikäyttöisempi vaihtoehto tuollaiselle sorttauksentekofunktiolla.

Eli esim.

function coordinateOrder (a, b) {
  if (a.x != b.x) return a.x - b.x;
  if (a.y != b.y) return a.y - b.y;
  return a.nimi.localeCompare(b.nimi);
}
[
  {"nimi": "Keke", "x": 1, "y": 1},
  {"nimi": "Pepe", "x": 1, "y": 0},
  {"nimi": "Xaxa", "x": 1, "y": 1},
  {"nimi": "Susu", "x": 0, "y": 1},
  {"nimi": "Mimi", "x": 0, "y": 0}
].sort(coordinateOrder);

Sinänsähän tämä on täsmälleen sama kuin Metabolixin viimeisessä esimerkissä, mutta vertailufunktio vaan on nimetty ja käytetävissä muissakin yhteyksissä.

The Alchemist [14.12.2020 09:54:25]

#

Niin, tekisin itsekin ennemmin Grezin menetelmällä eli vertailufunktioita käyttäen sen sijaan, että kloonataan koko lajittelutoimintoa n:ään eri funktioon... Minimalismia, selkeyttä, ennakoitavuutta, ylläpidettävyyttä. Tämä on myös js:n suunnittelijoiden idea ja juuri sen takia sort-funktiolle voi antaa argumenttina vertailufunktion.

Grez tosin rikkoo suositustaan heti omassa esimerkissään ja sotkee koordinaatteja lajittelevaan mekanismiin jotain muutakin kuin koordinaatteja...

function coordinateCompare (a, b) {
  return a.x - b.x || a.y - b.y
}

function nameCompare (a, b) {
  return a.localeCompare(b)
}

function objectCompare (a, b) {
  return coordinateCompare(a, b) || nameCompare(a, b)
}

function multiCompare (...comparators) {
  return (a, b) => {
    for (const compare of comparators) {
      const delta = compare(a, b)

      if (delta) {
        return delta
      }
    }

    return 0
  }
}

myObjects = [...]

myObjects.sort(objectCompare)
myObjects.sort(multiCompare(coordinateCompare, nameCompare))

Numeroiden ja lukuarvoja sisältävien merkkijonojen ns. luonnolliseen järjestämiseen voi käyttää myös javascriptin natiiveja toiminnallisuuksia.

const numbers = [2, 20, 11, 1]

numbers.sort(new Intl.Collator('en', { numeric: true }).compare)

console.log(numbers)

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta