Start learning free

LESSON 269 OF 678 · TUPLES, SORTING & BUILT-INS

Shortest Words First in Swift: Sorting with Two Rules

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

    SWIFT · TUPLES, SORTING & BUILT-INS

    Shortest Words First in Swift, explained

    Problem overview

    The function sortByLength receives an array of lowercase words and returns them from shortest to longest. Words of the same length go in alphabetical order. A word that appears twice stays twice.

    For ["banana", "fig", "apple", "kiwi"] the result is ["fig", "kiwi", "apple", "banana"]. For ["sun", "a", "moon", "sun"] it is ["a", "sun", "sun", "moon"].

    Approach

    Sorting by one rule is not enough, because words of equal length need a tie-breaker. sorted accepts a closure that receives two items, a and b, and returns true when a must go before b. Put the two rules in that order:

    1. If the lengths differ, the shorter word goes first.
    2. Otherwise, the word that comes first from A to Z goes first.

    The second rule runs only when the first one doesn't decide, which is exactly what a tie-breaker means.

    On the first example, the lengths are 6, 3, 5 and 4. The first rule alone puts the words in the order "fig", "kiwi", "apple", "banana". The second rule would matter if two of them had the same length, as "dog" and "cat" do in the second example, where the first rule says nothing and the alphabet decides.

    The order of the rules matters. If the alphabetical check came first, it would decide every comparison between two different words, and length would never be used. The more important rule always goes first.

    Step by step

    1. Call words.sorted with a closure that names its items a, b in. A closure with more than one line needs these names; $0 and $1 suit one-liners.
    2. First rule: if a.count != b.count, return a.count < b.count. count is the number of characters in a string.
    3. Second rule: for equal lengths, return a < b. On two strings, < is true when the first comes before the second in alphabetical order.
    4. Return ordered. Sorting only moves words around, so duplicates survive.

    The solution in Swift

    class Solution {
        func sortByLength(_ words: [String]) -> [String] {
            let ordered = words.sorted { a, b in
                if a.count != b.count {
                    return a.count < b.count
                }
                return a < b
            }
            return ordered
        }
    }
    

    Swift details:

    • sorted returns a new array. words itself is unchanged. The in-place version is sort, which needs a var array.
    • The closure returns a Bool: "does a go before b?" Two items that are equal should give false in both directions. That is why the rules use strict <, never <=.
    • String comparison is by character values. Uppercase letters sort before lowercase ones: ["pear", "Zed", "apple"].sorted() gives ["Zed", "apple", "pear"]. The lesson's words are all lowercase, so plain < gives A to Z.

    Edge cases and common mistakes

    • Only the second rule. sorted { $0 < $1 } alone gives alphabetical order and ignores length.
    • Only the first rule. A closure that returns just a.count < b.count orders by length but gives equal-length words no A-to-Z order: ["dog", "cat", "ant"] sorted that way comes out as ["dog", "cat", "ant"].
    • Duplicates. ["sun", "a", "moon", "sun"] keeps both "sun" entries.
    • Comparing lengths the wrong way. a.count > b.count puts the longest words first.
    • Sorting twice. Sorting alphabetically first and then by length alone does not reliably keep the alphabetical order inside each length. Put both rules in one closure instead.
    • Reversed second rule. return a > b for the tie-break gives Z to A inside each length.

    Testing

    • ["banana", "fig", "apple", "kiwi"] gives ["fig", "kiwi", "apple", "banana"].
    • ["dog", "cat", "ant", "bee"] gives ["ant", "bee", "cat", "dog"].
    • ["sun", "a", "moon", "sun"] gives ["a", "sun", "sun", "moon"].
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free