Waarden sorteren met JavaScript

Lijsten en tabellen zijn vaak de beste manier om gegevens op internet weer te geven; maar u moet zich geen zorgen maken over het handmatig sorteren van die informatie. In de tutorial van vandaag maak je een jQuery-plugin die al je eenden op een rij zet met JavaScript-gemak!


Voorwoord

Hoe werkt sortering precies in JavaScript? Het is niet ingewikkeld: elk array-object heeft een sorteermethode. Als u geen parameters doorgeeft, worden de objecten in de array geconverteerd naar tekenreeksen, worden ze pseudo-alfabetisch gesorteerd en geretourneerd. Meestal is dit verschrikkelijk; overweeg om de cijfers 0 - 10 alfabetisch te sorteren. Je zou dit krijgen: [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9]. Gelukkig kunnen we een functie doorgeven aan de sorteermethode. Die functie zou twee parameters moeten nemen (de twee items die vergeleken moeten worden): dan zal het 0 retourneren als ze gelijk zijn, een negatief getal als de eerste parameter voorgaat, of een positief getal van de tweede parameter als eerste. Dus getallen zijn eigenlijk het eenvoudigste om "handmatig" te sorteren:

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

Uiteraard zal dit 0 opleveren als de nummers gelijk zijn, een negatief getal als een moet de eerste zijn, en een positief getal als b moet de eerste zijn.

We gaan kijken naar het sorteren van verschillende soorten gegevens, een paar in meerdere indelingen; maar dit zal allemaal veel nuttiger zijn als we het in een jQuery-plug-in plaatsen, dus laten we beginnen met het opzetten van die shell!

De plug-in Shell

Als je niet bekend bent met het schrijven van jQuery-plug-ins, bekijk dan Screencast van Jeffrey Way "Je kunt nog steeds geen jQuery-plug-in maken?" U bent snel op de hoogte als u vertrouwd bent met jQuery! (ware bekentenis: ik heb eigenlijk nooit een plug-in geschreven totdat ik deze heb gemaakt).

We zullen onze plug-in, genaamd datasort, op deze manier instellen: we geven het een reeks items door om te sorteren; we kunnen vier parameters specificeren.

  • datatype (het type gegevens dat je sorteert)
  • sortElement (het onderliggende element dat u wilt sorteren op, indien gewenst)
  • sortAttr (het attribuut dat u wilt sorteren op, indien gewenst)
  • achteruit (de richting waarin ze moeten sorteren)

Een volledig aangepaste aanroep van onze plug-in ziet er dus als volgt uit:

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

Dit is de plug-in shell:

 (functie ($) $ .fn.datasort = function (options) var defaults = // stel de standaard parameterwaarden in datatype: 'alpha', sortElement: false, sortAttr: false, reverse: false, // combineer de standaard- en gebruikersparameters, de standaardinstellingen overschrijven = $ .extend (, standaardinstellingen, opties), datatypes = , base = , that = this; if (typeof settings.datatype === 'string')  that.sort (datatypes [settings.datatype]); if (typeof settings.datatype === 'function') that.sort (settings.datatype); if (settings.reverse) that = $ ($. makeArray (this) .reverse ()); $ .each (dat, functie (index, element) that.parent (). add (element););;) (jQuery);

Dus hier is hoe het werkt: we zullen aan het begin alle variabelen instellen. Als de parameter datatype een tekenreeks is, vinden we de bijbehorende sorteerfunctie in het datatype-object en sorteren ermee; als de parameter datatype een functie is, sorteren we ermee. Ten slotte, als de omgekeerde instelling op true is ingesteld, keren we de volgorde van de gesorteerde items om (aangezien jQuery-objecten geen echte JavaScript-arrays zijn, werkt de reverse-functie niet op hen, dus we kunnen $ .makeArray (gebruiken) ) om er een te maken, en als het eenmaal is omgekeerd, proberen we het opnieuw!).

Het leggen van een beetje meer grondwerk

Op het allerlaagste niveau kunt u bijna elk type gegevens op twee manieren sorteren: we zullen ze alfabetisch en numeriek noemen. Laten we deze twee functies maken als eigenschappen van uw basisobject.

 base = alpha: function (a, b) a = a.toUpperCase (); b = b.toUpperCase (); terugkeer (a < b) ? -1 : (a > b): 1: 0; // ternaire operator: toestand? returnIfTrue: returnIfFalse, number: function (a, b) a = parseFloat (a); b = parseFloat (b); terug a - b; ,

Best simpel, toch? Simpelweg de twee waarden normaliseren, vergelijken en retourneren. Het lastige gedeelte is het ontleden van de gegevens die we naar deze functies willen sturen; dat is wat we nu gaan doen. Er is echter nog een ding.

Bij het sorteren van items in de array, willen we misschien niet sorteren op de tekst van het element zelf. De sorteer- en sortAttr-parameters van onze plug-in zijn hiervoor. We zullen bijvoorbeeld waarschijnlijk tabelrijen willen sorteren op basis van een bepaalde kolom met tabelcellen. In dat geval gebruiken we $ ('table tr'). Datasort (sortElement: 'td.price'). Of misschien willen we een lijst met afbeeldingen sorteren op basis van hun alt-kenmerken: $ ('ul li'). Datasort (sortElement: 'img', sortAttr: 'alt'). Vanwege dit alles, moeten we nog een functie toevoegen aan ons basisobject:

 base = alpha: function (a, b) ..., getal: functie (a, b) ..., uittreksel: functie (a, b) var get = function (i) var o = $ (i ); if (settings.sortElement) o = o.children (settings.sortElement);  if (settings.sortAttr) o = o.attr (settings.sortAttr);  else o = o.text ();  terug o; ; return a: get (a), b: get (b); ,

Het ziet er misschien ingewikkeld uit, maar dat is het niet. We maken gewoon een jQuery-object met elk item; als sortElement is ingesteld, gebruiken we de methode children () om de juiste elementen te krijgen. Vervolgens krijgen we, als een sortAttr is ingesteld, de waarde ervan; zo niet, dan krijgen we de tekst van het element. We hebben dit allemaal op een innerlijke functie ingesteld en een object met twee eigenaardigheden geretourneerd; deze eigenschappen zijn de waarden die we moeten parseren en verzenden naar de juiste basissorteerfunctie.

Dit leek waarschijnlijk veel voorbereidend werk, maar wat we eigenlijk aan het doen waren, is zoveel mogelijk code ontleden. Op deze manier zullen ze veel minder herhalingscode zijn, omdat de belangrijke acties zijn weggebroken als functies.

Woorden en cijfers sorteren

We zijn eindelijk hier: het leuke gedeelte! We beginnen met het bouwen van twee eenvoudige functies voor ons datatypes-object. Deze geven eenvoudige waarden door aan base.extract () en geven die retourwaarden door aan de juiste sorteerklasse.

 datatypes = alpha: function (a, b) var o = base.extract (a, b); return base.alpha (o.a, o.b); , nummer: functie (a, b) var o = base.extract (a, b); for (var e in o) o [e] = o [e] .replace (/ [$]? (-? \ d +.? \ d +) /, '\ $ 1');  return base.number (o.a, o.b); ,,

Onze alfabetische sorteerder moet duidelijk zijn. De nummersorteerder doet iets meer: ​​voordat de geëxtraheerde waarden worden doorgegeven, wordt er een dollarteken aan de voorkant uitgeknipt. Ik heb deze reguliere expressie eenvoudig gehouden, maar je kunt hier veel verschillende getalnotaties ontleden als je complex wilt worden. Laten we onze evoluerende plug-in eens proberen; maak een basis html-pagina:

     Gegevens sorteren    
Voornaam Achternaam
Jeffrey Manier
Sean landarbeider
Adam Molenaar
Ian Yates
Adrian Proberen
Caleb Aylsworth
  • 4.09
  • 4.10
  • 67.8
  • 100
  • -98
  • 67.7
  • 23
  • $ 299,66
  • $ 299,57
  • $ 0.14
  • $ 80.00

Ik heb een tabel en twee lijsten opgenomen (en ik heb ze in het kort gestileerd). Houd rekening met onze plugin-aanroepen: we gebruiken het standaard gegevenstype voor de tabel, maar sorteren op de tabelcellen met een klasse van de laatste; probeer dit te veranderen in 'td.first.' Vervolgens sorteren we de lijsten numeriek en keren een ervan om. Dit is het bewijs van onze inspanningen:

Best aardig, maar dat waren relatief eenvoudige waarden; wat als we meerdere formaten voor één type willen kunnen sorteren?

Datums sorteren

Er zijn een aantal verschillende manieren om datums te schrijven, waardoor het vrij lastig is om ze te sorteren voor sorteren. We kunnen de meesten hiervan echter wel behandelen met:

 datum: functie (a, b) var o = base.extract (a, b); for (var e in o) o [e] = o [e] .replace (/ - / g, ") .replace (/ january | jan / i, '01') .replace (/ februari | feb / i , '02') .replace (/ march | mar / i, '03') .replace (/ april | apr / i, '04') .replace (/ may / i, '05') .replace (/ juni | jun / i, '06') .replace (/ juli | jul / i, '07') .replace (/ august | aug / i, '08') .replace (/ september | sept | sep / i, ' 09 ') .replace (/ oktober | oct / i,' 10 ') .replace (/ november | nov / i,' 11 ') .replace (/ december | dec / i,' 12 ') .replace (/ ( \ d 2) (\ d 2), (\ d 4) /, '\ $ 3 \ $ 1 \ $ 2') .replace (/ (\ d 2) \ / (\ d 2 ) \ / (\ d 4) /, '\ $ 3 \ $ 2 \ $ 1'); return base.number (oa, ob);, 

Dus wat doen we hier? Ten eerste, hier is de logica: als alle datums zijn opgemaakt in JJJJMMDD, zullen ze correct sorteren met numerieke sortering. Onze parser kan de volgende datumnotaties sorteren:

  • YYYY-MM-DD
  • YYYYMMDD
  • DD / MM / YYYY
  • maand DD, JJJJ

Eerst strippen we onze streepjes, waardoor YYYY-MM-DD gereed blijft voor parsen. Vervolgens vervangen we de naam of afkorting van elke maand met zijn getalswaarde. Ten slotte moeten we de nummers opnieuw rangschikken voor DD / MM / JJJ en maand DD, JJJJ. Dat is wat de laatste twee uitdrukkingen doen. Om dit eens te proberen, plakt u deze lijst in onze HTML:

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

En noem het hiermee:

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

Is dit een perfecte datum-parser? Helemaal niet; we kunnen DD / MM / YY niet sorteren, want er is geen manier om te weten in welke eeuw dit is. Ook kunnen we het verschil tussen DD / MM / JJ en MM / DD / JJ niet zien, dus we moeten gewoon kies er een.

Sorteertijd

Sorteertijdwaarden moeten een van de moeilijkste waarden zijn om te sorteren: we moeten 12-uurs tijd, 24-uursuren en waarden met of zonder AM / PM-tags en seconden kunnen accepteren. Ik denk dat het alfabet het gemakkelijkst te sorteren is, ook al zijn het allemaal cijfers. Waarom? Overweeg deze twee tijdstempels: 00:15:37 en 12:15. De eerste moet eerst komen, maar als we ze op nummer sorteren, worden ze geparseerd als drijvers en eindigen ze als 1537 en 1215. Nu komt de tweede waarde eerst. Ook hoeven we bij het alfabetisch sorteren de dubbele punten niet te verwijderen (parseFloat () zou zich ertussen verslikken). Dus hier is hoe het gedaan is.

 tijd: functie (a, b) var o = base.extract (a, b), afternoon = /^(.+) PM $ / i; for (var e in o) o [e] = o [e] .split (':'); var last = o [e] .lengte - 1; if (afternoon.test (o [e] [last])) o [e] [0] = (parseInt (o [e] [0]) + 12) .toString (); o [e] [laatste] = o [e] [laatste] .replace (middag, '\ $ 1');  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); 

Laten we dit regel voor regel doornemen.

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

We beginnen met onze variabelen: onze geëxtraheerde waarden en een reguliere expressie om te controleren op PM-label.

 for (var e in o) o [e] = o [e] .split (':'); var last = o [e] .lengte - 1; if (afternoon.test (o [e] [last])) o [e] [0] = (parseInt (o [e] [0]) + 12) .toString (); o [e] [laatste] = o [e] [laatste] .replace (middag, '\ $ 1'); 

Vervolgens starten we een lus, waarbij we elk van de waarden doorlopen die we sorteren; Eerst splitsten we het in een array bij de dubbele punten. We maken een eenvoudige manier om bij de laatste items van de array te komen: onze 'laatste' variabele. Vervolgens testen we onze PM-regex op het laatste item in onze array; als het true retourneert, heeft deze waarde de PM-tag. Daarom voegen we 12 toe aan het eerste item in onze array, wat de uurwaarde is; we doen dit omdat we alle waarden nodig hebben om binnen 24 uur te worden opgemaakt. (Merk op dat om dit te doen, we het naar een getal moeten converteren, 12 toevoegen en het dan weer in een string veranderen). Ten slotte gebruiken we opnieuw de PM-regex om dat label te verwijderen uit het laatste item in de array.

 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);

In dit laatste deel controleren we de uurwaarde voor twee voorwaarden: is dit minder dan 10? en heeft de string maar één karakter? Dit is belangrijk omdat een waarde als 08 wordt geparseerd als 8 en kleiner is dan 10; maar we proberen te zien of we een nul aan de voorkant moeten toevoegen. Als de string slechts één karakter heeft, dan voegen we de nul toe, dus 3 wordt 03. Dit zal de dingen op orde houden!

Voordat we ons bij de array voegen, verwijderen we AM-labels. Dus nu dit ...

 
  • 01:15:47
  • 15.45 uur
  • 00:00:17
  • 06:56
  • 19:39
  • 4:32 uur
  • 00:15:36

... kan hiermee worden gesorteerd ...

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

En we zijn klaar! Zie de vruchten van onze arbeid:

Meer willekeurige waarden

We hebben onze jQuery-plug-in zo ingesteld dat gebruikers sorteerfuncties kunnen doorgeven als de parameter datatype. Dit stelt ons in staat om de plug-in eenvoudig uit te breiden, hoewel we geen toegang hebben tot de basisklasse van de plugin-oproep. We kunnen gemakkelijk een functie schrijven om psudeo-beoordelingen te sorteren:

 $ ('ul.rating li'). datasort (datatype: function (a, b) var o = a: $ (a) .text (), b: $ (b) .text () for ( var e in o) o [e] = o [e] .replace (/ poor / i, 0) .replace (/ satisfactory / i, 1) .replace (/ goed / i, 2) .replace (/ uitstekend / i, 3); return oa - ob;);

Dit gebruikt de eenvoudigste reguliere expressies die mogelijk zijn om een ​​lijst als deze te sorteren:

 
  • Goed
  • Uitstekend
  • Arm
  • Bevredigend

Het zit er op!

Nu weet u wat: het sorteren van waarden in JavaScript is echt niet zo moeilijk als u misschien had gedacht. Je kunt je voorstellen dat het handig is om een ​​tafel te sorteren, met zoiets als dit:

 $ ('table # myTable thead th'). toggle (function () var $ this = $ (this); $ ('table # myTable tbody tr'). datasort (datatype: $ this.attr ('rel') ), sortElement: 'td.' + $ this.attr ('class'));, function () var $ this = $ (this); $ ('table # myTable tbody tr'). datasort ( datatype: $ this.attr ('rel'), sortElement: 'td.' + $ this.attr ('class'), reverse: true);); 

(Probeer hiermee de jQuery-code voor de tabel in het eerste voorbeeld te vervangen!)

Natuurlijk kunnen we deze plug-in veel verbeteren; We kunnen het bijvoorbeeld laten controleren rel atttribute voor een datatype als er geen als parameter wordt opgegeven en standaard op alpha als er geen is rel. Maar dat is afgezien van het sorteren.

Kortom, om te sorteren met JavaScipt, volgen we deze stappen:

  1. Bepaal de verschillende formaten die u wilt sorteren.
  2. Bepaal in welk formaat u wilt sorteren.
  3. Sorteer de reeks items met de methode sort (), waarbij u een functie doorgeeft die de twee items naar de gewenste indeling converteert voordat u ze vergelijkt

Heb je een datatype om toe te voegen aan onze plug-in? Heb je een betere manier om een ​​van deze te sorteren? Laten we het horen in de reacties!

  • Volg ons op Twitter of abonneer je op de Nettuts + RSS Feed voor de beste tutorials voor webontwikkeling op internet.