Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Jenny Shaw
Jenny Shaw

Posted on

     

Sorting out Javascript Sort

You Don't Know Sort

Until recently, I'd really underestimated how much Javascript'ssort method can accomplish.

It's a tool that can easily be taken for granted, especially in those circumstances when you can just call it on an array and, without any extra effort at all, watch it magically rearrange its elements in order as you expect it to.

// Orders names alphabeticallyletmyPets=["Oreo","Shortbread","Peanut","Souffie","Tella"];pets.sort();// => [ 'Oreo', 'Peanut', 'Shortbread', 'Souffie', 'Tella' ]// Order numbers numericallyletnumbers=[3,2,1,6,5,4];numbers.sort();// => [ 1, 2, 3, 4, 5, 6 ]// Don't stop reading here! You know nothing yet!

However,sort on its own doesn't exactly behave as one would expect it to. For example, when called onamazingInventions, an array of capitalized and lowercased words,sort will order all the capitalized words before the lowercased ones. It's a bit odd and a little inconvenient, but I can see that there'ssome logic involved, so I'm not ready to riot over it yet.

letamazingInventions=['computer','Internet','telegraph','vaccines'];amazingInventions.sort();// returns . . . . . . . [ 'Internet', 'computer', 'telegraph', 'vaccines' ]// when you expected . . [ 'computer', 'Internet', 'telegraph', 'vaccines' ]

The function also appears to order numbers until you introduce multi-digit and negative numbers into your arrays, and that's when you really start to notice that something's not quite right.

letcomplexNumbers=[1,3,-2,-1,5,11];complexNumbers.sort();// returns . . . . . . . [ -1, -2, 1, 11, 3, 5 ]// when you expected . . [ -2, -1, 1, 3, 5, 11 ]

In the example above,sort places-1 before-2, and inserts11 between1 and3. That order clearly makes no sense, so how does this happen?

How Sort Works

It turns out that Javascript'ssort sorts numbers just like a dictionary sorts words. Remember when you were a kid and learned how to alphabetize words letter by letter from left to right?sort is doing the same thing here. And regardless of whether your input is an array of strings or numbers or both, it'll interpret each element like a string and will systematically order elements one character unit at a time according to itsUnicode code point.

Let's take a look into this for ourselves. Below, we have an array containing a variety of characters. It includes one lowercase and one uppercase letter, single and double-digit numbers, and we'll also toss in a dollar sign for good measure.

letrandomStuff=["a","A","1","2""12","$"];

To make the point clearer, I'll usecharCodeAt() and create a handy reference that points each element to itsfirst character's character code. Don't worry about the process, but just pay attention to the return.

charCodes={}for(letelofrandomStuff){charCodes[el]=el.charCodeAt(0)}// => {// '1':  49,// '2':  50,// '12': 49,// '$':  36,//  A:   65,//  a:   97 }

You'll notice that1 and12 share the same first character,1, therefore each also shares the samefirst character code,49. So by this logic of comparing first characters only,12 would be ordered before2 becausesort is using12's first digit's character code to compare it with2's.

Let's sort the array using only.sort(), and we'll get this return.

arr.sort();// => [ '$', '1', '12', '2', 'A', 'a' ]

So, understanding thatsort looks at elements character by character and compares by character code, it makes sense that capitalA would come before lowercasea and that$ would be first in line before everything else.sort is still in a way rearranging elements in numerical order, but strictly by each character's character code. Yes, the result still looks wonky, but at least we understand now it's not completely random and that it does follow a predictable set of rules.

Let's Sort Stuff!

Now that we've made more sense of.sort(), we can really use it to its fullest potential by taking advantage of the fact that it is ahigher-order function. I'll try not to sound super repetitive while explaining this but, a higher-order function is a type of function that can take another function as an argument or has a return value that is itself a function. Some examples of other common higher-order functions we use areforEach,map,filter, andreduce.

In the context ofsort, we want to pass in a "compare function", and the best part about being able to do that is that we can really makesort do precisely what we want, whether that be sorting array elements purely alphabetically, numerically, or by properties. We can do quite a lot!

Sort Purely Alphabetically

I was an English teacher in a past life, so it really bugs me to see words "alphabetized" by uppercase then lowercase letters. It's not how you'd see it in a dictionary, so it's no reason to letsort get away with this kind of behavior.

To fix the faulty alphabetical ordering ofsort, our compare function will do the following:

  • Compare words, two at a time
  • Lowercase words before comparisons to prevent division between lower and uppercased words! (Note: this won't affect the element in the end, but it sets pairs up nicely for a fair comparison)
  • Apply the following logic: If worda's character code is lower than wordb's, return-1, else return1

The logic we apply is important here because the return value determines how we'll sort each element. A negative return means thata should be sorted beforeb and a positive return means thatb should be sorted beforea.

letpWords=["Paris","panic","potato","Portugal"]pWords.sort()// applying only .sort()// => [ 'Paris', 'Portugal', 'panic', 'potato' ] -- BAD.// create compare functionfunctioncompareWords(a,b){if(a.toLowerCase()<b.toLowerCase()){return-1;}else{return1;}}// pass compareWords function into .sort()pWords.sort(compareWords)// => [ 'panic', 'Paris', 'Portugal', 'potato' ] -- MUCH BETTER.

That's exactly how I want it sorted and I feel so much better. And just because I prefer my code to look succinct, I might slim it down using an arrow function and a ternary operator.

pWords.sort((a,b)=>a.toLowerCase()<b.toLowerCase()?-1:1)

Nice!

To sort in reverse-alphabetical order, just reverse the comparison operator.

pWords.sort((a,b)=>a.toLowerCase()>b.toLowerCase()?-1:1)

Numeric Order

A compare function that orders numeric arrays uses the same concept ascompareWords. Again, we'll compare two elementsa andb, but this time using the subtraction operator.

Similarly, if the difference returns a negative value,a is sorted beforeb, if the difference returns a positive value,b is sorted beforea.

letnumbers=[1,101,23,18]// You could do it this wayfunctioncompareNumbers(a,b){returna-b;}numbers.sort(compareNumbers);// But this is much simplernumbers.sort((a,b)=>a-b);// => [ 1, 18, 23, 101 ]

Order by Word Length

We can get a little more creative here and instead of ordering alphabetically, we can order by word length. Remember how we sorted numbers? It's a lot like that. We're not comparing letters anymore, but we're comparing thenumber of characters in a word, which is why the order of "Paris" and "panic" don't matter.

pWords.sort((a,b)=>a.length-b.length)// => [ 'Paris', 'panic', 'potato', 'Portugal' ]

Ordering objects by property

This is wheresort gets really fun. Imagine we've got an array of objects. I created an array containing a small sampling ofMcDonald's burgers. Included in each object is the name of the burger, the calorie count, and a general list of ingredients that make the burger.

I can sort this array of burgers in a number of different ways, each by a different property. First, I'll sort alphabetically by burger name.

To do this, we'll follow the structure of our alphabetical or numerical compare functions, but this time, we'll chain a property name of our objects to our variablesa andb.

letMcDBurgers=[{name:"hamburger",calories:250,ingredients:["bun","beef patty","ketchup","pickle slices","onions","mustard"]},{name:"Cheeseburger",calories:300,ingredients:["bun","beef patty","american cheese","ketchup","pickle slices","onions","mustard"]},{name:"McChicken",calories:410,ingredients:["bun","chicken patty","lettuce","mayonnaise"]},{name:"Filet-O-Fish",calories:390,ingredients:["bun","fish filet patty","american cheese","tartar sauce"]}];// Sort by burger nameMcDBurgers.sort((a,b)=>a.name.toLowerCase()<b.name.toLowerCase()?-1:1)//=> [//  {//    name: 'Cheeseburger',//    calories: 300,//    ...//  },//  {//    name: 'Filet-O-Fish',//    calories: 390,//    ...//  },//  {//    name: 'hamburger',//    calories: 250,//    ...//  },//  {//    name: 'McChicken',//    calories: 410,//    ...//  }//]

Look! Our burger objects are neatly ordered alphabetically!

I can go even further and order them by calorie count or by count of unique ingredients.

// sort by calorie countMcDBurgers.sort((a,b)=>a.calories-b.calories)// sort by number of unique ingredientsMcDBurgers.sort((a,b)=>a.ingredients.length-b.ingredients.length)

If we were to run these lines, we should be able to see each of McDBurger's burger objects rearrange accordingly. Plug each line of code into your console and see for yourself what returns come back! Then try to explore what other ways you can manipulatesort to order your arrays!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Fullstack Software Engineer, queer Taiwanese American, native Bronxite, and expert level nomnomnommer.
  • Location
    Seattle
  • Education
    Flatiron School
  • Work
    Fullstack Junior Software Engineer
  • Joined

More fromJenny Shaw

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp