Problem overview
The method sortByLength gets an array of words and returns it sorted from shortest to longest. Words of the same length go in alphabetical order, A to Z. A word that appears twice stays twice. The array has 1 to 100 words of 1 to 20 lowercase letters.
For ["banana", "fig", "apple", "kiwi"] the result is ["fig", "kiwi", "apple", "banana"]: lengths 3, 4, 5, 6. For ["dog", "cat", "ant", "bee"] all lengths are 3, so the result is ["ant", "bee", "cat", "dog"]. For ["sun", "a", "moon", "sun"] it is ["a", "sun", "sun", "moon"].
Approach
The array method sort accepts a compare function that takes two items, a and b, and returns a number. A negative number means a goes first, a positive number means b goes first, and 0 means they are tied. Here there are two rules, and the second only matters when the first ends in a tie:
- If the lengths differ, the shorter word goes first: return
a.length - b.length. That is negative whenais shorter. - Otherwise the lengths are equal, so order the words alphabetically with
a.localeCompare(b).
Why a compare function is needed
Without one, sort() turns items into strings and compares them as text, which gives A to Z order and ignores length. And for numbers it gives [10, 9, 1].sort() equal to [1, 10, 9]. Always pass a compare function when the order is not plain text order.
Step by step
- Call
words.sortwith an arrow function ofaandband a body in braces. The first line is anif: whena.length !== b.length, returna.length - b.length. - Below the
if, returna.localeCompare(b)for words of the same length. - After the sort, return
words.
The solution in TypeScript
class Solution {
sortByLength(words: string[]): string[] {
words.sort((a, b) => {
if (a.length !== b.length) {
return a.length - b.length;
}
return a.localeCompare(b);
});
return words;
}
}
sort changes the array it is called on and also returns it. That is why the method can return words after the call: it is now in order. If you need to keep the original order, sort a copy, for example [...words].sort(...).
localeCompare returns a negative number, 0 or a positive number, which is exactly what a compare function needs. For lowercase letters it is plain A to Z order. It also compares mixed-case words the way a dictionary does: ["B", "a", "C"].sort() gives ["B", "C", "a"] (capitals first), while sorting with localeCompare gives ["a", "B", "C"]. This lesson has only lowercase words, so either is fine here.
Duplicates need no special care. Sorting rearranges items and never drops them, and two equal words are simply tied (localeCompare returns 0).
Tracing the third example
In ["sun", "a", "moon", "sun"], comparing "a" with "sun": the lengths 1 and 3 differ, so the first rule returns a negative number and "a" goes first. Comparing "sun" with "moon": 3 and 4 differ, so "sun" goes first. Comparing the two "sun" words: the lengths match, and localeCompare returns 0, so they stay tied. The result is ["a", "sun", "sun", "moon"].
Testing
| words | Result |
|---|---|
["banana", "fig", "apple", "kiwi"] | ["fig", "kiwi", "apple", "banana"] |
["dog", "cat", "ant", "bee"] | ["ant", "bee", "cat", "dog"] |
["sun", "a", "moon", "sun"] | ["a", "sun", "sun", "moon"] |
Common mistakes
- Calling
sort()with no function. The words come out in A to Z order, not by length. - Writing the rules the wrong way round. Checking the letters first would sort A to Z and only use the length for identical words.
- Reversing the subtraction.
b.length - a.lengthputs the longest word first. - Expecting a copy. The sorted array and the original are the same array.
