Nivå opp din .sorter spill

0
44

Sortering er en super hendig JavaScript-metode som kan vise verdier i en matrise i en bestemt rekkefølge. Om det er real estate oppføringer av pris, burger ledd av avstand, eller beste i nærheten lykkelige timer ved klassifisering, sortering av matriser av informasjon er et felles behov.

Hvis du allerede gjør dette med JavaScript på et prosjekt, du vil sannsynligvis bruke den innebygde utvalg .sorter-metoden, som er i samme familie utvalg av metoder som inkluderer .filter .kart og .redusere.

La oss ta en titt på hvordan du gjør det!

En rask notat om bivirkninger

Før du går i detaljer på hvordan du kan bruke .sorter, det er en veldig viktig detalj som må tas opp. Mens mange av ES5 utvalg metoder, for eksempel .filter .kart og .redusere returnere en ny matrise og la det opprinnelige urørt, .sorter vil sortere tabellen i stedet. Hvis dette er uønsket, en ES6 teknikk for å unngå dette er å bruke spredning operatør til konsist opprette en ny tabell.

const foo = [‘c’,’b’,’a’];
const bar = [‘x’,’z’,’y’];
const fooSorted = foo.sorter();
const barSorted = [bar…].sorter();

– konsollen.logg({foo, fooSorted, bar, barSorted});

/*
{
“foo”: [ “a”, “b”, “c” ],
“fooSorted”: [ “a”, “b”, “c” ],
“bar”: [ “x”, “z”, “y” ],
“barSorted”: [ “x”, “y”, “z” ]
}
*/

foo og fooSorted både referanse samme rekke, men bar og barSorted er nå enkelte tabeller.

Generell oversikt

Den eneste parameteren av .sorter metode er en funksjon. Spec refererer til dette som compareFn — jeg vil referere til det som “sammenligningen funksjon” for resten av innlegget. Denne sammenligningen funksjonen aksepterer to parametere, som jeg vil referere til som a og b. a og b er de to elementene som vi vil sammenligne. Hvis du ikke oppgir en sammenligning funksjon, utvalg vil tvinge hvert element i en streng og sortere i henhold til Unicode-poeng.

Hvis du ønsker at en må bestilles først i tabellen, sammenligningen funksjonen skal returnere et negativt heltall; for b, et positivt heltall. Hvis du ønsker de to å opprettholde deres gjeldende ordre, returnerer 0.

Hvis du ikke forstår, trenger du ikke å bekymre deg! Forhåpentligvis vil det bli mye mer klar med et par eksempler.

Å sammenligne tall

En av de enkleste tilbakering å skrive er en rekke forhold.

const tall = [13,8,2,21,5,1,3,1];
const byValue = (a,b) => a – b;
const sortert = […tall].sorter(byValue);
– konsollen.logg(sortert); // [1,1,2,3,5,8,13,21]

Hvis a er større enn b, a – b vil returnere et positivt tall, så b vil bli sortert først.

Sammenligning av strenger

Når man sammenligner strenger > og < operatører vil sammenligne verdier basert på hver streng er Unicode-verdi. Dette betyr at alle store bokstaver vil være “mindre” enn alle små bokstaver, noe som kan føre til uventet oppførsel.

JavaScript har en metode for å hjelpe til med å sammenligne strenger: Strengen.prototypen.localeCompare metode. Denne metoden aksepterer en sammenligning string, en innstilling, og et valg objekt. Alternativene objekt aksepterer noen egenskaper (som du kan se her), men jeg synes at den mest nyttige er “følsomhet.” Dette vil påvirke hvordan sammenligninger arbeid mellom bokstav-varianter som for eksempel fall og aksent.

const strenger = [‘Om’, ‘alfa’, ‘Mot’, ‘om’, ‘uber’, ‘Uber’, ‘Alfa’, ‘iver’];

const sortBySensitivity = sensitivitet => (a, b) => en.localeCompare(
b,
udefinerte // locale string — udefinert betyr å bruke standard nettleser
{ følsomhet }
);

const byAccent = sortBySensitivity(‘aksent’);
const byBase = sortBySensitivity (“base”);
const byCase = sortBySensitivity(‘saken’);
const byVariant = sortBySensitivity(‘variant’); // standard

const accentSorted = […strenger].sorter(byAccent);
const baseSorted = […strenger].sorter(byBase);
const caseSorted = […strenger].sorter(byCase);
const variantSorted = […strenger].sorter(byVariant);

– konsollen.logg({accentSorted, baseSorted, caseSorted, variantSorted});

/*
{
“accentSorted”: [ “alpha”, “Alfa”, “uber”, “Uber”, “Über”, “über”, “Iver”, “iver” ],
“baseSorted”: [ “alpha”, “Alfa”, “Über”, “über”, “uber”, “Uber”, “Iver”, “iver” ],
“caseSorted”: [ “alpha”, “Alfa”, “über”, “uber”, “Über”, “Uber”, “iver”, “Iver” ],
“variantSorted”: [ “alpha”, “Alfa”, “uber”, “Uber”, “über”, “Über”, “iver”, “Iver” ]
}
*/

For meg, baseSorted synes å være den mest logisk for de fleste alfabetisk sortering — ‘ü’, ‘u’, ‘Ü’, og ‘U’ er tilsvarende, slik at de forblir i den rekkefølgen av den opprinnelige matrisen.

Kjører funksjoner før du sammenligne verdier

Du kan være lurt å kjøre en sammenligning funksjon på en verdi som er avledet fra hvert utvalg er element. Først, la oss skrive en sammenligning funksjon fabrikk som vil “kart” over elementet før du ringer til sammenligning funksjon.

const sortByMapped = (kart,compareFn) => (a,b) => compareFn(kart(a),kart(b));

En use case for dette er sortering basert på den egenskap av et objekt.

const kjøp = [
{ name: ‘Popcorn’, pris: blir 5,75 },
{ name: ‘Film Billett’, pris: 12 },
{ name: ‘Brus’, pris: 3.75 },
{ name: ‘Godteri’, pris: 5 },
];

const sortByMapped = (kart,compareFn) => (a,b) => compareFn(kart(a),kart(b));
const byValue = (a,b) => a – b;
const toPrice = e => e.pris;
const byPrice = sortByMapped(toPrice,byValue);

– konsollen.log ([kjøp…].sorter(byPrice));

/*
[
{ name: “Brus”, pris: 3.75 },
{ name: “Candy”, pris: 5 },
{ name: “Popcorn”, pris: blir 5,75 },
{ name: “Film-Billett”, pris: 12 }
]
*/

Et annet tilfelle kan være å sammenligne et utvalg av datoer.

const datoer = [‘2018-12-10’, ‘1991-02-10’, ‘2015-10-07’, ‘1990-01-11’];
const sortByMapped = (kart,compareFn) => (a,b) => compareFn(kart(a),kart(b));
const toDate = e => ny Dato(e).getTime();
const byValue = (a,b) => a – b;
const byDate = sortByMapped(toDate,byValue);

– konsollen.log([…datoer].sorter(byDate));
// [“1990-01-11”, “1991-02-10”, “2015-10-07”, “2018-12-10”]

Reversering av et slags

Det er noen tilfeller der du ønsker kanskje å snu utfallet av en sammenligning funksjon. Dette er litt forskjellig enn å gjøre en form og deretter snu resultatet i veien bånd skal behandles: hvis du snu utfallet, bånd vil også bli reversert i orden.

Å skrive en høyere orden funksjon som aksepterer en sammenligning funksjon og returnerer en ny en, vil du trenger for å snu tegnet av den sammenligningen er returverdien.

const flipComparison = fn => (a,b) => -fn(a,b);
const byAlpha = (a,b) => en.localeCompare(b, null, { følsomhet: ‘base’ });
const byReverseAlpha = flipComparison(byAlpha);

– konsollen.log([‘A’, ‘B’, ‘C’].sorter(byReverseAlpha)); // [‘C’,’B’,’A’]

Kjører en tiebreaker sortere

Det er tider når du kan være lurt å ha en “tie-breaker” liksom — det er en annen sammenligning funksjon som er brukt i tilfelle av et slips.

Ved å bruke [].redusere, du kan slå sammen en rekke sammenligningen funksjoner til en eneste en.

const sortByMapped = map => compareFn => (a,b) => compareFn(kart(a),kart(b));
const flipComparison = fn => (a,b) => -fn(a,b);
const byValue = (a,b) => a – b;

const byPrice = sortByMapped(e => e.prisen)(byValue);
const byRating = sortByMapped(e => e.rating)(flipComparison(byValue));

const sortByFlattened = fns => (a,b) =>
fns.redusere((acc, fn) => acc || fn(a,b), 0);

const byPriceRating = sortByFlattened([byPrice,byRating]);

const restauranter = [
{ name: “Foo’ s Burger Stå”, pris: 1, rating: 3 },
{ name: “Tapas-Bar”, pris: 3, rating: 4 },
{ name: “Baz Pizza”, pris: 3, rating: 2 },
{ name: “Fantastisk Deal”, pris: 1, rating: 5 },
{ name: “Overpriced”, pris: 5, rating: 1 },
];

– konsollen.logg(restauranter.sorter(byPriceRating));

/*
{name: “Fantastisk Deal”, pris: 1, rating: 5}
{name: “Foo’ s Burger Stå”, pris: 1, rating: 3}
{name: “Tapas-Bar”, pris: 3, rating: 4}
{name: “Baz Pizza”, pris: 3, rating: 2}
{name: “Overpriced”, pris: 5, rating: 1}
*/

Å skrive en tilfeldig sortere

Du ønsker kanskje å sortere en array “tilfeldig.” En teknikk som jeg har sett på, er å bruke følgende funksjon som sammenligningen funksjon.

const byRandom = () => Matematikk.random() – .5;

Siden Matematikk.random() returnerer en “tilfeldige” tall mellom 0 og 1, byRandom funksjonen skal returnere et positivt tall halvparten av tiden og et negativt tall for den andre halvparten. Dette virker som det ville være en god løsning, men dessverre, siden den sammenligningen er ikke funksjonen “konsekvent” — noe som betyr at det kan ikke gå tilbake samme verdi når som kalles flere ganger med de samme verdiene — det kan resultere i noen uventede resultater.

For eksempel, la oss ta en matrise med tall mellom 0 og 4. Hvis dette byRandom funksjon var helt tilfeldig, det ville være ventet at den nye indeksen for hvert nummer vil bli spredt ut like over nok iterasjoner. Den opprinnelige 0 verdi ville være like sannsynlig å være i indeks 4 som indeks 0 i den nye tabellen. Men, i praksis, med denne funksjonen vil bias hvert nummer til sin opprinnelige posisjon.

Se Penn
Tabellen.sorter() Tilfeldig 👎 av Adam Giese (@AdamGiese)
på CodePen.

Den “diagonale” fra øverst til venstre, vil du statistisk sett har størst verdi. I en ideell og helt tilfeldig sortere, hver tabellcelle ville sveve rundt 20%.

Hurtigreparasjonen for dette er å finne en måte å sikre at sammenligningen funksjonen forblir konsistent. En måte å gjøre dette på er å kartlegge tilfeldig verdi for hver matrise-elementet før den sammenligningen, da kartet det bort etter.

const sortByMapped = map => compareFn => (a,b) => compareFn(kart(a),kart(b));
const verdier = [0,1,2,3,4,5,6,7,8,9];
const withRandom = (e) => ({ random: Matematikk.random(), original: e });
const toOriginal = ({opprinnelige}) => original;
const toRandom = ({random}) => tilfeldig;
const byValue = (a,b) => a – b;
const byRandom = sortByMapped(toRandom)(byValue);

const shuffleArray = array => array
.kart(withRandom)
.sorter(byRandom)
.kart(toOriginal);

Dette sikrer at hvert element har en enkelt tilfeldig verdi som er bare beregnet en gang per element, snarere enn en gang per sammenligningen. Dette fjerner sortering bias mot den opprinnelige posisjon.

Se Penn
Tabellen.sorter() Tilfeldig 👍 av Adam Giese (@AdamGiese)
på CodePen.