Start learning free

LESSON 264 OF 671 · DESTRUCTURING, SORTING & BUILT-INS

Shortest Words First in JavaScript: Custom sort

Practice Read the full explanation

You get an array of words, words. Return the words sorted from shortest to longest.

Words of the same length go in alphabetical order, from A to Z.

A word can appear more than once. Every copy stays in the result.

 

Example 1:

Input: words = ["banana", "fig", "apple", "kiwi"]
Output: ["fig", "kiwi", "apple", "banana"]
Explanation: "fig" has 3 letters and "kiwi" has 4.
"apple" has 5 and "banana" has 6.

Example 2:

Input: words = ["dog", "cat", "ant", "bee"]
Output: ["ant", "bee", "cat", "dog"]
Explanation: All four words have 3 letters.
So they go from A to Z.

Example 3:

Input: words = ["sun", "a", "moon", "sun"]
Output: ["a", "sun", "sun", "moon"]
Explanation: "sun" appears twice, so it stays twice.
Both copies come after "a" and before "moon".

 

Constraints:

  • words has 1 to 100 words.
  • Each word has 1 to 20 lowercase letters.
STEP 1 OF 3
    Ctrl + Enter to check

    JAVASCRIPT · DESTRUCTURING, SORTING & BUILT-INS

    Shortest Words First in JavaScript, explained

    Problem overview

    sortByLength(words) receives an array of lowercase words and returns them sorted from shortest to longest. Words of the same length go in alphabetical order. A repeated word stays repeated.

    For ["banana", "fig", "apple", "kiwi"] the answer is ["fig", "kiwi", "apple", "banana"]. For ["sun", "a", "moon", "sun"] it is ["a", "sun", "sun", "moon"]: both copies of "sun" stay.

    Approach: a compare function with two rules

    Array.prototype.sort takes an optional compare function. It gets two items, a and b, and returns a number:

    • negative: a goes before b;
    • positive: b goes before a;
    • zero: the two are equal for sorting purposes.

    Without a compare function, sort compares items as strings. For numbers that gives surprising results: [10, 9, 1].sort() gives [1, 10, 9]. For words it would give plain alphabetical order, which ignores length. So this lesson gives sort its own rule.

    The rule has two parts. First compare lengths: if they differ, return a.length - b.length. That is negative when a is shorter, so the shorter word goes first. Only when the lengths are equal does the second rule matter: return a.localeCompare(b), which is negative, zero or positive according to alphabetical order.

    Step by step

    1. Call words.sort with an arrow function of a and b, with a body in braces.
    2. The first line of the body handles different lengths: if (a.length !== b.length), then return the difference.
    3. The next line is the tie-break for equal lengths: return a.localeCompare(b).
    4. After the sort, return words.

    The solution in JavaScript

    class Solution {
        sortByLength(words) {
            words.sort((a, b) => {
                if (a.length !== b.length) {
                    return a.length - b.length;
                }
                return a.localeCompare(b);
            });
            return words;
        }
    }
    

    sort changes the array in place and also returns it, so words is already in order after the call. The tests only look at the returned array. If you need to keep the original order, sort a copy: [...words].sort(...).

    The three examples give:

    ["fig","kiwi","apple","banana"]
    ["ant","bee","cat","dog"]
    ["a","sun","sun","moon"]
    

    What the compare function returns

    Calling the rule by hand shows how it decides. With the function above as cmp, a shorter word first gives a negative number and a longer one a positive number, and for equal words the result is 0:

    cmp("fig", "kiwi")  -> -1   (fig is shorter, so it goes first)
    cmp("kiwi", "fig")  ->  1
    cmp("cat", "ant")   ->  1   (same length, "ant" comes first)
    cmp("sun", "sun")   ->  0   (equal, so both copies stay)
    

    The exact positive or negative number can differ (the length rule returns the difference, such as -1 or 5), so only its sign matters. localeCompare follows the language's alphabetical rules, which for lowercase letters a to z is plain A to Z.

    Complexity

    The sort does O(n log n) comparisons for n words, and each comparison costs at most the length of a word, which is at most 20 here.

    Edge cases and common mistakes

    • Forgetting the compare function. words.sort() alone orders alphabetically, so "banana" would come before "fig".
    • Using only the length rule. Words of equal length would then keep the order they started in. A sort with only a.length - b.length turns ["dog", "cat", "ant", "bee"] into ["dog", "cat", "ant", "bee"] again, not A to Z.
    • Using === or a boolean. A compare function must return a number. Returning a.length > b.length gives true or false, which sort reads as 1 or 0, never negative. On ["banana", "fig", "apple", "kiwi"] that comparison leaves the array as ["banana", "fig", "apple", "kiwi"], unsorted.
    • Dropping duplicates. Sorting only moves items. Both copies of a repeated word stay.
    Keep going with JavaScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free