Sortere verdier med JavaScript

Lister og tabeller er ofte den beste måten å vise data på nettet. men du bør ikke bekymre deg for å sortere den informasjonen manuelt. I dagens veiledning skal du lage et jQuery-plugin som vil sette alle dine ender på rad med JavaScript-lette!


Forord

Så, hvordan sorterer du akkurat i JavaScript? Det er ikke så komplisert: et hvilket som helst arrayobjekt har en sorteringsmetode. Hvis du ikke overfører noen parametere, vil den konvertere objekter i arrayet til strenger, sortere dem pseudo-alfabetisk, og returnere dem. Vanligvis er dette forferdelig; vurder å sortere tallene 0 - 10 alfabetisk. Du vil få dette: [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9]. Heldigvis kan vi overføre en funksjon til sorteringsmetoden. Denne funksjonen skal ta to parametere (de to elementene som skal sammenlignes): da vil det returnere 0 hvis de er like, et negativt tall hvis den første parameteren har forrang, eller et positivt nummer av den andre parameteren skal komme først. Så tall er faktisk det enkleste å sortere "manuelt":

 numberArray.sort (funksjon (a, b) return a - b);

Tydeligvis vil dette returnere 0 hvis tallene er like, et negativt tall hvis en bør være første, og et positivt tall hvis b bør være først.

Vi skal se på sortering av flere forskjellige typer data, noen i flere formater; men dette vil alle være mye mer nyttig hvis vi pakker det inn i et jQuery-plugin, så la oss starte med å sette opp det skallet!

Plugin Shell

Hvis du ikke er kjent med å skrive jQuery-plugins, sjekk ut Jeffrey Way's Screencast "Du kan fortsatt ikke opprette et jQuery-plugin?" Det vil gi deg fart på kort tid hvis du er komfortabel med jQuery! (ekte bekjennelse: Jeg hadde faktisk aldri skrevet et plugin før jeg lagde dette).

Vi setter opp pluginet, kalt datasort, på denne måten: Vi sender det en rekke elementer som skal sorteres; Vi kan spesifisere fire parametere.

  • datatype (typen data du sorterer)
  • sortElement (barnelementet du vil sortere etter, hvis ønskelig)
  • sortAttr (attributet du vil sortere etter, hvis ønskelig)
  • omvendt (retningen de burde sortere inn i)

Så et fullstendig endret anrop til plugin-modulen kan se slik ut:

 $ ('ul.names li) .datasort (datatype:' alpha ', sortElement:' span.first ', sortAttr:' rel ', revers: true);

Her er plugin shell:

 (funksjon ($) $ .fn.datasort = funksjon (alternativer) var defaults = // angi standard parameterverdier datatype: 'alpha', sortElement: false, sortAttr: false, reverse: false standard og brukerparametere, overordnede standardinnstillinger = $ .extend (, standardinnstillinger, alternativer), datatyper = , base = , det = dette; hvis (type av settings.datatype === 'streng')  that.sort (datatyper [settings.datatype]); hvis (typeof settings.datatype === 'funksjon') that.sort (settings.datatype); hvis (settings.reverse) that = $ ($. makeArray (this) .reverse ()); $ .each (som, funksjon (indeks, element) that.parent (). append (element););;) (jQuery);

Så her er hvordan det vil fungere: Vi vil sette opp alle variablene i begynnelsen. Da, hvis datatypeparameteren er en streng, finner vi den tilsvarende sorteringsfunksjonen i datatyperobjektet og sorterer med den; Hvis datatypeparameteren er en funksjon, sorterer vi med den. Endelig, hvis omvendt innstilling er satt til sant, reverserer vi rekkefølgen på de sorterte elementene (siden jQuery-objekter ikke er ekte JavaScript-arrays, vil reverseringsfunksjonen ikke fungere på dem, så vi kan bruke $ .makeArray ( ) for å gjøre det til en, så når det er reversert, re-jquery-fy det!).

Legger litt mer grunnarbeid

På det aller laveste nivået kan du sortere nesten alle typer data på en av to måter: vi ringer dem alfabetisk og numerisk. La oss lage disse to funksjonene som egenskapene til basisobjektet ditt.

 base = alfa: funksjon (a, b) a = a.toUpperCase (); b = b.toUpperCase (); returnere (a < b) ? -1 : (a > b): 1: 0; // ternær operatør: tilstand? returnIfTrue: returnIfFalse, nummer: funksjon (a, b) a = parseFloat (a); b = parseFloat (b); returner a - b; ,

Ganske enkelt, eh? Bare normaliser de to verdiene, sammenlign og returner. Den vanskelige delen er å analysere dataene vi vil sende til disse funksjonene; det er det vi skal gjøre nå. Det er imidlertid en ting til.

Når du sorterer elementer i matrisen, vil vi kanskje ikke bare sortere etter selve elementets tekst. Parameterene blackElement og sortAttr i plugin-modulen er til dette formål. For eksempel vil vi sannsynligvis ønske å sortere tabellrader basert på en bestemt kolonne av tabellceller. I så fall vil vi bruke $ ('table tr'). Datasort (sortElement: 'td.price'). Eller kanskje vil vi sortere en liste med bilder med deres alt attributter: $ ('ul li'). Datasort (sortElement: 'img', sortAttr: 'alt'). På grunn av alt dette, må vi legge til en funksjon til vårt basisobjekt:

 base = funksjonalitet (a, b) ..., tall: funksjon (a, b) ..., ekstrakt: funksjon (a, b) var get = funksjon (i) var o = $ ); hvis (settings.sortElement) o = o.children (settings.sortElement);  hvis (settings.sortAttr) o = o.attr (settings.sortAttr);  ellers o = o.text ();  returnere o; ; returnere a: get (a), b: get (b); ,

Det kan se komplisert ut, men det er det ikke. Vi lager bare et jQuery-objekt med hvert element; Hvis sortElement er satt, bruker vi child () -metoden for å få de riktige elementene. Så, hvis en sortAttr er satt, får vi verdien. Hvis ikke, får vi elementets tekst. Vi har satt alt dette til en indre funksjon, og returnere et objekt med to properites; Disse egenskapene er verdiene vi må analysere og sende til riktig base sorteringsfunksjon.

Dette virket sannsynligvis som mye prep-arbeid, men det vi virkelig gjorde, er å trekke så mye kode som mulig. På denne måten vil de være mye mindre gjenta kode, fordi de viktige handlingene er blitt samlet sammen som funksjoner.

Sortering av ord og tall

Vi er endelig her: den morsomme delen! Vi starter med å bygge to enkle funksjoner for vårt datatyperobjekt. Disse vil enkelt passere verdier til base.extract () og deretter sende disse returverdiene til riktig sorteringsklasse.

 datatyper = alfa: funksjon (a, b) var o = base.extract (a, b); returnere base.alpha (o.a., o.b); , tall: funksjon (a, b) var o = base.extract (a, b); for (var e i o) o [e] = o [e] .replace (/ [$]? (-? \ + +? \ d +) /, '\ $ 1');  returnere base.number (o.a., o.b); ,,

Vår alfabetisk sortering bør være åpenbar. Nummer sorteringen gjør litt mer: før du overfører de ekstraherte verdiene på, stripes den ut et dollarskilt foran. Jeg har holdt dette vanlige uttrykket enkelt, men du kunne analysere mange forskjellige tallformater her hvis du ønsket å bli kompleks. La oss gi vår utviklende plugin et forsøk; lag en grunnleggende HTML-side:

     Datasortering    
Fornavn Etternavn
Jeffrey Vei
Sean Hodge
Adam Miller
Ian Yates
Adrian Prøve
Caleb Aylsworth
  • 4,09
  • 4.10
  • 67.8
  • 100
  • -98
  • 67.7
  • 23
  • $ 299,66
  • $ 299,57
  • $ 0.14
  • $ 80.00

Jeg har tatt med et bord og to lister (og jeg har stylet dem kort). Vær oppmerksom på våre plugin-samtaler: vi bruker standard datatype for tabellen, men sorterer ved tabellcellene med en klasse sist; prøv å endre dette til 'td.first.' Deretter sorterer vi lister numerisk, og reverserer en av dem. Her er beviset på vårt arbeid:

Ganske fint, men det var relativt enkle verdier; hva om vi vil kunne sortere flere formater for en type?

Sortering Datoer

Det finnes en rekke forskjellige måter å skrive datoer på, noe som gjør det ganske vanskelig å analysere dem for sortering. Vi kan imidlertid dekke de fleste av dem med dette:

 dato: funksjon (a, b) var o = base.extract (a, b); for (var i o) o [e] = o [e] .replace (/ - / g, ") .replace (/ januar | jan / i, '01') .replace (/ februar | feb / i , '02') .replace (/ mars / i, '03') .replace (/ april | apr / i, '04') .replace (/ mai / jeg, '05') .replace (/ juni juni / juni, juni) .replace (/ juli | juli / jeg, 07) .replace (/ august | aug / jeg, '08') .replace (/ september | september | sep / i, ' 09.) .replace (/ oktober | oktober / jeg, '10') .replace (/ november | nov / i, '11') .replace (/ desember | desember / \ d 2) (\ d 2), (\ d 4) /, '\ $ 3 \ $ 1 \ $ 2') .replace (/ (\ d 2) \ /  \ / (\ d 4) /, '\ $ 3 \ $ 2 \ $ 1'); returnere base.number (oa, ob);, 

Så hva gjør vi her? Først, her er logikken: Hvis alle datoene er formatert YYYYMMDD, vil de sortere riktig med numerisk sortering. Vår parser kan sortere følgende datoformater:

  • ÅÅÅÅ-MM-DD
  • ÅÅÅÅMMDD
  • DD / MM / ÅÅÅÅ
  • måned DD, ÅÅÅÅ

Først striper vi våre bindestreker, som vil forlate YYYY-MM-DD klar for parsing. Deretter erstatter vi hver måned navn eller forkortelse med tallverdien. Til slutt må vi omarrangere tallene for DD / MM / ÅÅÅÅ og måned DD, ÅÅÅÅ. Det er det de to siste uttrykkene gjør. For å prøve dette, lim inn denne listen i vår HTML:

 
  • 2009-10-06
  • 25. september 1995
  • 1990-06-18
  • 20100131
  • 18. juni 2009
  • 02/11/1993
  • 15941219
  • 1965-08-05
  • 1425-12-25

Og ring det med dette:

 $ ('ul.date li'). datasort (datatype: 'date');

Er dette en perfekt date parser? Ikke på noen måte; Vi kan ikke sortere DD / MM / YY, fordi det ikke er mulig å vite hvilket århundre dette er i. Vi kan heller ikke fortelle forskjellen mellom DD / MM / YY og MM / DD / YY, så vi må bare Velg en.

Sorteringstid

Sortering av tidverdier må være en av de vanskeligste verdiene som skal sorteres: Vi må kunne akseptere 12-timers tid, 24-timers tid og verdier med eller uten AM / PM-koder og sekunder. Jeg tror det er lettest å sortere tid alfabetisk, selv om det er alle tallene. Hvorfor? Vurder disse to tidsstemplene: 00:15:37 og 12:15. Den første skal komme først, men hvis vi sorterer dem etter nummer, blir de analysert som flyter, og ender opp som 1537 og 1215. Nå kommer den andre verdien først. Også når vi sorterer alfabetisk, trenger vi ikke å ta ut kolonene (parseFloat () ville kvele på dem). Så her er hvordan det er gjort.

 tid: funksjon (a, b) var o = base.extract (a, b), ettermiddag = / ^ (.+) PM $ / i; for (var e i o) o [e] = o [e] .split (':'); var sist = o [e] .lengde - 1; hvis (ettermiddag.test (o [e] [siste])) o [e] [0] = (parseInt (o [e] [0]) + 12) .toString (); o [e] [siste] = o [e] [siste] .replace (ettermiddag, '\ $ 1');  hvis (parseInt (o [e] [0]) < 10 && o[e][0].length === 1)  o[e][0] = '0' + o[e][0];  o[e][last] = o[e][last].replace(/^(.+) AM$/i, '\$1'); o[e] = o[e].join(");  return base.alpha(o.a, o.b); 

La oss gå gjennom denne linjen etter linje.

 var o = base.extract (a, b), ettermiddag = / ^ (.+) PM $ / i;

Vi starter med våre variabler: våre ekstraherte verdier og et vanlig uttrykk for å sjekke PM-etiketten.

 for (var e i o) o [e] = o [e] .split (':'); var sist = o [e] .lengde - 1; hvis (ettermiddag.test (o [e] [siste])) o [e] [0] = (parseInt (o [e] [0]) + 12) .toString (); o [e] [siste] = o [e] [siste] .replace (ettermiddag, '\ $ 1'); 

Deretter starter vi en forløp, går gjennom hver av verdiene vi sorterer; Først delte vi det i en gruppe på kolonene. Vi lager en enkel måte å komme til de siste elementene i arrayet: vår "siste" variabel. Deretter tester vi vår PM regex på det siste elementet i vårt utvalg; Hvis den returnerer sann, har denne verdien PM-merket. Derfor legger vi 12 til det første elementet i vårt array, som vil være timeverdien; Vi gjør dette fordi vi trenger alle verdiene som skal formateres i 24-timers tid. (Merk at for å gjøre dette må vi konvertere det til et tall, legge til 12, og deretter slå det tilbake til en streng). Til slutt bruker vi PM regex igjen for å fjerne den etiketten fra det siste elementet i matrisen.

 if (parseInt (o [e] [0]) < 10 && o[e][0].length === 1)  o[e][0] = '0' + o[e][0];  o[e][last] = o[e][last].replace(/^(.+) AM$/i, '\$1'); o[e] = o[e].join(");  return base.alpha(o.a, o.b);

I dette siste stykket, sjekker vi timverdien for to forhold: er det mindre enn 10? og har strengen bare ett tegn? Dette er viktig fordi en verdi som 08 vil analysere som 8 og være mindre enn 10; men vi prøver å se om vi må legge til null på forsiden. Hvis strengen har bare ett tegn, legger vi til null, så 3 blir 03. Dette vil holde tingene i orden!

Før vi slår seg sammen i arrayet, fjerner vi alle AM-etiketter. Så nå er dette ...

 
  • 01:15:47
  • 3:45 PM
  • 12:00:17
  • 06:56
  • 19:39
  • 4:32 AM
  • 00:15:36

... kan sorteres med dette ...

 $ ('ul.time li'). datasort (datatype: 'tid'); 

Og vi er ferdige! Se fruktene av vårt arbeid:

Flere tilfeldige verdier

Vi har satt opp vår jQuery-plugin slik at brukerne kan passere sorteringsfunksjoner som datatype-parameteren. Dette gjør at vi enkelt kan utvide pluginet, selv om vi ikke har tilgang til basen "klassen" fra plugin-samtalen. Vi kan enkelt skrive en funksjon for å sortere psudeo-klassifiseringer:

 $ ('ul.rating li') datasort (datatype: funksjon (a, b) var o = a: $ (a) .text (), b: $ (b) .text () for var / i, o) o [e] = o [e] .replace (/ poor / i, 0) .place (/ tilfredsstillende / jeg, 1) .replace (/ good / i, 2) .place / jeg, 3); returnere oa - ob;);

Dette bruker de enkleste regulære uttrykkene som er mulige for å sortere en liste som dette:

 
  • Flink
  • Utmerket
  • Fattige
  • Tilfredsstillende

Det er en Wrap!

Nå er du i vet: sorteringsverdiene i JavaScript er egentlig ikke så vanskelig som du kanskje har trodd. Du kan forestille deg at dette er nyttig å sortere et bord, med noe som dette:

 $ ('tabell # myTable tame tr'). datasort (datatype: $ this.attr ('rel' ), sortElement: 'td' '+ $ this.attr (' class ');, funksjon () var $ this = $ (dette); $ (' tabell # myTable tbody tr '). datasort  datatype: $ this.attr ('rel'), sortElement: 'td.' + $ this.attr ('class'), revers: true);); 

(Prøv å erstatte jQuery-koden for tabellen i det første eksemplet med dette!)

Selvfølgelig kan vi forbedre denne plugin mye; for eksempel kan vi få det til å sjekke rel atttribute for en datatype hvis en ikke er gitt som en parameter, og standard til alfa hvis det ikke er noe rel. Men det er bortsett fra sorteringen.

Til sammen, for å sortere med JavaScipt, følger vi disse trinnene:

  1. Bestem de forskjellige formatene du vil sortere.
  2. Bestem hvilket format du vil sortere inn.
  3. Sorter oppvalget av elementer med typen (), passerer inn i en funksjon som konverterer de to elementene til ønsket format før du sammenligner dem

Har du en datatype for å legge til plugin? Har du en bedre måte å sortere en av disse på? La oss høre det i kommentarene!

  • Følg oss på Twitter, eller abonner på Nettuts + RSS-feed for de beste webutviklingsopplæringene på nettet.