Sortera värden med JavaScript

Listor och tabeller är ofta det bästa sättet att visa data på webben; men du borde inte behöva oroa dig för att sortera informationen manuellt. I dagens handledning kommer du att göra ett jQuery-plugin som sätter alla dina ankor i rad med JavaScript-lätthet!


Förord

Så, hur exakt sorterar du jobb i JavaScript? Det är inte för komplicerat: ett array-objekt har en sorteringsmetod. Om du inte skickar några parametrar överför den objekten i arrayen till strängar, sorterar dem pseudo-alfabetiskt och returnerar dem. Vanligtvis är detta hemskt; överväg att sortera siffrorna 0-10 i alfabetisk ordning. Du skulle få detta: [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9]. Lyckligtvis kan vi överföra en funktion till sortmetoden. Den funktionen ska ta två parametrar (de två objekten som ska jämföras): Då kommer den att returnera 0 om de är lika, ett negativt tal om den första parametern har företräde eller ett positivt tal för den andra parametern bör komma först. Så siffror är faktiskt det enklaste att sortera "manuellt":

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

Självklart kommer detta att returneras 0 om siffrorna är lika, ett negativt tal om en bör vara första och ett positivt tal om b bör vara första.

Vi ska titta på att sortera flera olika typer av data, några i flera format; men det här kommer alla att vara mycket mer användbart om vi sätter i det i ett jQuery-plugin, så låt oss börja med att konfigurera det skalet!

Plugin Shell

Om du inte är bekant med att skriva jQuery-plugins, kolla Jeffrey Way's Screencast "Du kan fortfarande inte skapa ett jQuery-plugin?" Det kommer att få dig till snabbhet på nolltid om du är bekväm med jQuery! (sann bekännelse: Jag hade faktiskt aldrig skrivit ett plugin tills jag gjorde den här).

Vi ställer in vårt plugin, kallat datasort, så här: vi skickar det en rad olika objekt att sortera; vi kan ange fyra parametrar.

  • datatyp (vilken typ av data du sorterar)
  • sortElement (det barnelement du vill sortera efter, om så önskas)
  • sortAttr (attributet du vill sortera efter, om så önskas)
  • omvänd (den riktning de ska sortera i)

Så ett fullständigt modifierat samtal till vårt plugin kan se ut så här:

 $ ('ul.names li) .datasort (datatype:' alfa ', sortElement:' span.first ', sortAttr:' rel ', omvänd: true);

Här är plugin-skalet:

 (funktion ($) $ .fn.datasort = funktion (alternativ) var standard = // ställa in standardparametervärden datatyp: 'alfa', sortElement: false, sortAttr: false, reverse: false standardvärdena och användarens parametrar, övergripande standardinställningar = $ .extend (, standardvärden, alternativ), datatyper = , bas = , det = detta; om (typ av settings.datatype === 'sträng')  that.sort (datatyper [settings.datatype]); om (typof settings.datatype === 'funktion') that.sort (settings.datatype); om (settings.reverse) that = $ ($. makeArray (this) .reverse ()); $ .each (som, funktion (index, element) that.parent (). append (element););;) (jQuery);

Så här är hur det ska fungera: Vi ställer in alla variablerna i början. Då, om datatypparametern är en sträng, hittar vi motsvarande sortfunktion i datatypobjektet och sorterar med det; Om datatypparametern är en funktion, sorterar vi med den. Slutligen, om omvänd inställning är satt till sant, vänder vi om ordningen för de sorterade objekten (eftersom jQuery-objekt inte är sanna JavaScript-arrays, kommer omvänd funktion inte att fungera på dem, så vi kan använda $ .makeArray ( ) för att förvandla den till en, så när den är omvänd, gör vi det igen!).

Lägger lite mer grundarbete

På den lägsta nivån kan du sortera nästan vilken typ av data som helst på ett av två sätt: vi ringer dem alfabetiskt och numeriskt. Låt oss skapa dessa två funktioner som egenskaper för ditt basobjekt.

 bas = alfa: funktion (a, b) a = a.toUpperCase (); b = b.toUpperCase (); returnera (a < b) ? -1 : (a > b): 1: 0; // ternär operatör: villkor? returnIfTrue: returnIfFalse, nummer: funktion (a, b) a = parseFloat (a); b = parseFloat (b); returnera a - b; ,

Ganska enkelt, va? Normalt normalisera de två värdena, jämföra och returnera. Den knepiga delen analyserar de data som vi vill skicka till dessa funktioner; det är vad vi ska göra nu. Det finns dock en sak till.

När du sorterar objekt i matrisen kanske vi inte vill sortera helt enkelt genom själva elementets text. Parametrarna sortElement och sortAttr i vårt plugin är för detta ändamål. Till exempel kommer vi troligen att vilja sortera tabellrader baserat på en viss kolumn tabellceller. I så fall skulle vi använda $ ('table tr'). Datasort (sortElement: 'td.price'). Eller kanske vill vi sortera en lista med bilder med deras alt attribut: $ ('ul li'). Datasort (sortElement: 'img', sortAttr: 'alt'). På grund av allt detta måste vi lägga till en ytterligare funktion till vårt basobjekt:

 bas = alfa: funktion (a, b) ..., nummer: funktion (a, b) ..., extrakt: funktion (a, b) var get = funktion (i) var o = $ ); om (settings.sortElement) o = o.children (settings.sortElement);  om (settings.sortAttr) o = o.attr (settings.sortAttr);  annat o = o.text ();  returnera o; ; returnera a: get (a), b: get (b); ,

Det kan se komplicerat ut, men det är det inte. Vi skapar bara ett jQuery-objekt med varje objekt; Om sortElement är inställt använder vi barnen () -metoden för att få de rätta elementen. Då, om en sortAttr är inställd, får vi dess värde; Om inte, får vi elementets text. Vi har satt allt detta till en inre funktion och returnerar ett objekt med två properites; Dessa egenskaper är de värden vi måste analysera och skicka till lämplig bassorteringsfunktion.

Det verkade förmodligen som en hel del prep-arbete, men det vi verkligen gjorde är att abstrahera så mycket kod som möjligt. På så sätt kommer de att vara mycket mindre upprepa kod, eftersom de viktiga åtgärderna har buntats bort som funktioner.

Sortera ord och nummer

Vi är äntligen här: den roliga delen! Vi börjar med att bygga två enkla funktioner för vårt datatyperobjekt. Dessa kommer enkelt att skicka värden till base.extract () och sedan skicka dessa returvärden till lämplig sorteringsklass.

 datatyper = alfa: funktion (a, b) var o = base.extract (a, b); returnera base.alpha (o.a, o.b); , tal: funktion (a, b) var o = base.extract (a, b); för (var i o) o [e] = o [e] .replace (/ [$]? (-? \ + +? \ d +) /, '\ $ 1');  returnera base.number (o.a., o.b); ,,

Vår alfabetiska sorterare ska vara uppenbar. Nummer sorteraren gör lite mer: innan de överför de extraherade värdena på det, ser det ut en dollar skylt fram. Jag har hållit det här regelbundna uttrycket enkelt, men du kunde analysera många olika nummerformat här om du ville bli komplex. Låt oss prova vårt utvecklingsprogram. skapa en grundläggande html-sida:

     Datasortering    
Förnamn Efternamn
Jeffrey Sätt
Sean Hodge
Adam Mjölnare
Ian Yates
Adrian Prova
caleb Aylsworth
  • 4,09
  • 4,10
  • 67,8
  • 100
  • -98
  • 67,7
  • 23
  • $ 299,66
  • $ 299,57
  • $ 0,14
  • $ 80.00

Jag har inkluderat ett bord och två listor (och jag har stylat dem kortfattat). Notera våra plugin-samtal: vi använder standarddatatypen för tabellen, men sorterar efter tabellcellerna med en klass av sista; försök ändra detta till "td.first." Sedan sorterar vi listorna numeriskt och vänd en av dem. Här är beviset på vårt arbete:

Ganska trevligt, men det var relativt enkla värden; vad händer om vi vill kunna sortera flera format för en typ?

Sortering Datum

Det finns ett antal olika sätt att skriva datum, vilket gör det ganska knepigt att analysera dem för sortering. Men vi kan täcka de flesta av dem med detta:

 datum: funktion (a, b) var o = base.extract (a, b); för / var / i , '02') .plats (/ mars / mars, '03') .plats (/ april | april / ik, '04') .plats (/ maj / juni / juli, juni) .replace (/ juli | jul / jag, '07') .replace (/ augusti | aug / jag, '08') .replace (/ september | september | sep / i, ' 09.) .plats (/ oktober | okt / i, '10 ') .plats (/ november | nov / jag,' 11 ') .plats (/ december | dec / jag,' 12 ') \ d 2) (\ d 2), (\ d 4) /, '\ $ 3 \ $ 1 \ $ 2') .replace (/ (\ d 2) \ /  \ / (\ d 4) /, '\ $ 3 \ $ 2 \ $ 1'); returnera base.number (oa, ob);, 

Så vad gör vi här? Först här är logiken: om alla datum är formaterade ÅÅÅÅMMDD, kommer de att sortera korrekt med numerisk sortering. Vår parser kan sortera följande datumformat:

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

Först förstår vi våra bindestreck, vilket lämnar YYYY-MM-DD redo för analysering. Sedan byter vi varje månad namn eller förkortning med sitt nummervärde. Slutligen måste vi omordna siffrorna för DD / MM / ÅÅÅÅ och månad DD, ÅÅÅÅ. Det är vad de två sista uttrycken gör. För att försöka klistra in den här listan i vår HTML:

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

Och kalla det med det här:

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

Är det här en perfekt date parser? Inte på något sätt; vi kan inte sortera DD / MM / YY, eftersom det inte finns något sätt att veta vilket sekel det här är. Vi kan inte heller skilja skillnaden mellan DD / MM / YY och MM / DD / YY, så vi måste bara Välj en.

Sorteringstid

Sortera tidsvärden måste vara ett av de svåraste värdena att sortera: Vi måste kunna acceptera 12-timmars tid, 24-timmars tid och värden med eller utan AM / PM-taggar och sekunder. Jag tycker att det är lättast att sortera tid alfabetiskt, trots att det är alla siffror. Varför? Tänk på dessa två tidsstämplar: 00:15:37 och 12:15. Den första ska komma först, men om vi sorterar dem efter nummer kommer de att tolkas som flottor och slutar som 1537 och 1215. Nu kommer det andra värdet först. När vi sorterar alfabetiskt, behöver vi inte ta ut kolonerna (parseFloat () skulle kväva på dem). Så här är hur det är gjort.

 tid: funktion (a, b) var o = base.extract (a, b), eftermiddag = /^ (.+) PM $ / i; för (var e i o) o [e] = o [e] .split (':'); var sist = o [e]. längd - 1; om (eftermiddag.test (o [e] [sista])) o [e] [0] = (parseInt (o [e] [0]) + 12) .toString (); o [e] [sista] = o [e] [sista] .plats (eftermiddag, '\ $ 1');  om (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); 

Låt oss gå igenom denna linje för rad.

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

Vi börjar med våra variabler: våra extraherade värden och ett regelbundet uttryck för att kontrollera PM-etiketten.

 för (var e i o) o [e] = o [e] .split (':'); var sist = o [e]. längd - 1; om (eftermiddag.test (o [e] [sista])) o [e] [0] = (parseInt (o [e] [0]) + 12) .toString (); o [e] [sista] = o [e] [sista] .plats (eftermiddag, '\ $ 1'); 

Därefter börjar vi en för loop, går igenom alla värdena vi sorterar; Först delade vi det i en matris vid kolonerna. Vi skapar ett enkelt sätt att komma till de sista objekten i arrayen: vår "sista" variabel. Då testar vi vår PM regex på sista objektet i vår array; Om det returneras sant, har detta värde PM-koden. Därför lägger vi 12 till det första objektet i vår array, vilket blir timvärdet. det gör vi för att vi behöver alla värden som ska formateras i 24-timmars tid. (Observera att för att göra det måste vi konvertera det till ett nummer, lägga till 12 och sedan ändra det till en sträng). Slutligen använder vi PM regex igen för att ta bort den etiketten från det sista objektet 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 denna sista bit kontrollerar vi timmarsvärdet för två villkor: är det mindre än 10? och har strängen bara ett tecken? Detta är viktigt eftersom ett värde som 08 kommer att tolka som 8 och vara mindre än 10; men vi försöker se om vi behöver lägga till en noll på framsidan. Om strängen bara har ett tecken lägger vi till noll, så blir 3 03. Detta kommer att hålla saker i ordning!

Innan vi går med i gruppen, tar vi bort alla AM-etiketter. Så nu är det här ...

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

... kan sorteras med detta ...

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

Och vi är klara! Se frukterna av vårt arbete:

Fler slumpmässiga värden

Vi har satt upp vårt jQuery-plugin så att användare kan klara sorteringsfunktioner som datatypparametern. Detta gör att vi enkelt kan förlänga pluginet, även om vi inte har tillgång till basen "klass" från plugin-samtalet. Vi kan enkelt skriva en funktion för att sortera psudeo-betyg:

 $ ('ul.rating li'). datasort (datatype: funktion (a, b) var o = a: $ (a) .text (), b: $ (b) .text () för var / i, o) o [e] = o [e] .plats (/ fattig / jag, 0) .plats (/ tillfredsställande / jag, 1) .plats (/ bra / jag, 2) / jag, 3); returnera oa - ob;);

Detta använder de enklaste regelbundna uttryck som är möjliga för att sortera en lista så här:

 
  • Bra
  • Excellent
  • Fattig
  • Tillfredsställande

Vi är klara!

Nu är du i vet: sorteringsvärdena i JavaScript är verkligen inte så svårt som du kanske har tänkt. Du kan tänka dig att det är användbart att sortera ett bord med något sådant:

 $ 'table # myTable tbody tr'). datasort (datatyp: $ this.attr ('rel' ), sortElement: 'td' + $ this.attr ('class');, funktion () var $ this = $ (detta); $ ('tabell # myTable tbody tr'). datasort  datatyp: $ this.attr ('rel'), sortElement: 'td.' + $ this.attr ('class'), omvänd: true);); 

(Försök byta jQuery-koden för tabellen i det första exemplet med detta!)

Naturligtvis kan vi förbättra denna plugin mycket; till exempel kan vi få det att kontrollera rel attttribute för en datatyp om en inte anges som en parameter och standard till alfa om det inte finns någon rel. Men det är bortsett från sorteringen.

Sammanfattningsvis, för att sortera med JavaScipt följer vi dessa steg:

  1. Bestäm de olika formaten du vill sortera.
  2. Bestäm vilket format du vill sortera i.
  3. Sortera arrayen av objekt med typen (), som passerar in i en funktion som konverterar de två objekten till önskat format innan de jämförs

Har en datatyp att lägga till i vårt plugin? Har du ett bättre sätt att sortera en av dessa? Låt oss höra det i kommentarerna!

  • Följ oss på Twitter, eller prenumerera på Nettuts + RSS-flödet för de bästa webbutvecklingsstudierna på webben.