Concepts / Sorting Dictionaries by Value

Sorting Dictionaries by Value

Tuple sorting compares the first element first, so the word controls the primary ordering.

  • Programming
Interactive lab

Try it: Sorting with key= and reverse=

How sorted() and list.sort() compute key(item) once per item, order the items by those keys with Python's comparison rules, and keep equal keys in their original order — including py4e's sort-a-dict-by-value pattern.

How it works

  1. Decorate: call the key function once on every item (no key means the item itself is compared).
  2. Compare keys with Python's rules: ints by value, strs by code point ('Z' < 'a'), tuples item by item.
  3. Order the items by key; reverse=True puts the largest key first.
  4. Stable: items with equal keys keep their original relative order, also with reverse=True.
  5. sorted() returns a new list; list.sort() rearranges the list in place and returns None; [:N] keeps the first N.

Default run (12 steps): 5 items in their original order. Python will call lambda t: t[1] once per item. … sorted() returned a new list: result = [('fig', 5), ('kiwi', 5), ('pear', 3), ('apple', 3), ('date', 1)].

Simplified: Up to 10 items of one kind (ints, short ASCII words, (word, count) tuples or a small dict) and a fixed menu of key functions. The sorting algorithm itself (Timsort) is not shown — only what it guarantees: the order by key and stability.

Educational simulation

Loading the simulation…

Interactive lab

Try it: Counting Words with a Dictionary

The py4e histogram pattern: split each line into words, count them with counts[word] = counts.get(word, 0) + 1, then find the most common word with a max loop or by sorting (count, word) tuples in reverse.

How it works

  1. Optionally remove punctuation with str.translate and lower-case the line, then split() it into words.
  2. For each word: counts.get(word, 0) returns the current count or 0 for a new word; store it plus one (or the if word not in counts form).
  3. A dict remembers insertion order: a word's position is fixed the first time it is seen.
  4. Max loop: keep the word whose count is strictly larger than the best so far (the first word wins a tie).
  5. Or build [(count, word) …], sort with reverse=True (ties ordered by the word, reversed) and slice [:N].

Default run (25 steps): Start with an empty dict. The text has 3 lines. … [:3] keeps the top 3: [(5, 'the'), (2, 'sat'), (2, 'on')].

Simplified: Printable-ASCII text up to 320 characters, 8 lines and 60 words, typed in the lab instead of read from a file or URL.

Educational simulation

Loading the simulation…

From Web Text to Ranked Words

Suppose you download a text file and want to discover which words occur most often. Counting manually is tedious and error-prone. A Python program can retrieve the text, process every word, store frequencies in a dictionary, and then prepare those dictionary entries for sorting by frequency.

The complete task has two connected parts. First, urllib.request.urlopen() opens a web URL and returns a file-like object. You can iterate over that object line by line as you would iterate over a local file. Second, you transform the text into a dictionary whose keys are words and whose values are total occurrence counts. To sort by those counts, you rebuild each dictionary entry in the opposite order: (value, key) instead of (key, value).

The Counting Pipeline

Web URLFile-like responselines as bytesDecode linereadable stringSplit into wordsone word at a timeWord-count dictionaryword to total countopeneach linesplitupdate
How does text move from a remote URL through line and word processing into dictionary entries?

The response from urlopen() is file-like, but each line arrives as bytes. Decode each line to a string before splitting it into words. The outer loop handles lines; the inner loop handles the words within each line. Every word then updates the same counts dictionary, so its value represents the total across the entire text rather than only the current line.

Code walkthrough
import urllib.request

counts = {}

with urllib.request.urlopen(url) as response:
    for line in response:
        text = line.decode()
        for word in text.split():
            counts[word] = counts.get(word, 0) + 1
  1. 1
    The response can be iterated over line by line.
  2. 2
    decode() converts each line from bytes into a string.
  3. 3
    split() supplies the individual words for the inner loop.
  4. 4
    counts.get(word, 0) returns the current count when the word exists and 0 when it does not.
  5. 5
    Adding 1 initializes a new word at 1 or increments an existing word.
  • Open the remote URL and receive a file-like response.
  • Start with an empty dictionary.
  • Read one byte line at a time.
  • Decode the bytes into readable text.
  • Visit each word in the decoded line.
  • Use the existing count or 0, then add 1.

Tracing Each Dictionary Update

A useful way to understand the counter is to pause after every word. The source uses the first two lines But soft what and light through yonder as a small tracing example. Each new word receives a count of 1 because it has not appeared before. If a word appears again later, the dictionary retrieves its current count and increases it.

Empty dictionary{}But{'But': 1}soft{'But': 1, 'soft': 1}what{'But': 1, 'soft': 1,'what': 1}light{'But': 1, 'soft': 1,'what': 1, 'light': 1}through{'But': 1, 'soft': 1,'what': 1, 'light': 1,'through': 1}yonder{'But': 1, 'soft': 1,'what': 1, 'light': 1,'through': 1, 'yonder': 1}process Butprocess softprocess whatprocess lightprocess throughprocess yonder
How does the word-count dictionary change as each word is processed?
Worked example

A Repeated Word

Process the words red, blue, red using the update counts[word] = counts.get(word, 0) + 1.

  1. 1
    red

    red is not present, so counts.get('red', 0) returns 0 and the stored count becomes 1.

  2. 2
    blue

    blue is not present, so its stored count becomes 1.

  3. 3
    red again

    red is already present with count 1, so the expression stores 2.

Result

The final dictionary is {'red': 2, 'blue': 1}.

Making Values Sortable

A word-count dictionary naturally stores entries as (key, value): the word is the key and its frequency is the value. If you place those entries directly into a list, the word appears first in each tuple. Tuple sorting compares the first element first, so the word controls the primary ordering.

wordkeycountfirst tuple elementcountvaluewordsecond tuple elementmove positionmove position
How does changing each entry from (key, value) to (value, key) make the dictionary value the first sorting criterion?

The solution is to reconstruct every entry as (value, key). For word counts, that means (count, word). Now the count occupies the first tuple position, so a simple sorted() call compares counts first. The word moves to the second position and can be used when two counts are equal.

python
Output
[(1, 'blue'), (2, 'green'), (2, 'red')]

Reading Tuple Sort Order

(1, blue)value 1(1, blue)smallest value(2, red)value 2(2, green)tie resolved by key(2, green)value 2(2, red)tie resolved by keycompare valuecompare value, then keycompare value, then key
Given (value, key) tuples, what order does sorting produce, and what happens when values tie?

What do you think happens?

What will sorted() produce from [(3, 'oak'), (1, 'ash'), (3, 'elm')]?

  • [(1, 'ash'), (3, 'elm'), (3, 'oak')]

  • [(3, 'oak'), (3, 'elm'), (1, 'ash')]

  • [(1, 'ash'), (3, 'oak'), (3, 'elm')]

Reveal answer
Answer:

[(1, 'ash'), (3, 'elm'), (3, 'oak')]

The first tuple element is compared first, so 1 comes before 3. The two tuples beginning with 3 tie on the first element, so their second elements are compared: elm comes before oak.

Tuple comparison is lexicographic: Python examines the first element first. If the first elements differ, that decides the order. If they are equal, comparison continues to the next element. Therefore, with (count, word) tuples, counts determine the main order and words determine the order within equal-count groups.

Mistakes in Counting and Sorting

  • Common mistake

    Processing the byte line as though it were already a string

    What it looks like

    Splitting line immediately without decoding it

    Why it fails

    Each line from the web source arrives as bytes, so it must be decoded before it can be treated as readable text and split into words.

    Do this instead

    Decode the line first, then split the resulting string.

  • Common mistake

    Resetting the count instead of incrementing it

    What it looks like

    counts[word] = 1 for every encounter

    Why it fails

    A repeated word is assigned 1 again, so its total occurrence count never accumulates.

    Do this instead
    Use counts[word] = counts.get(word, 0) + 1.
  • Common mistake

    Updating a different dictionary key than the current word

    What it looks like

    Using a fixed key while looping through words

    Why it fails

    Occurrences are collected under the wrong entry, so the dictionary no longer maps each word to its own total.

    Do this instead

    Use the current word as the key in the update expression.

  • Common mistake

    Sorting the original key-value pairs when the goal is value order

    What it looks like

    Sorting entries shaped as (word, count)

    Why it fails

    The word is first, so it controls the primary tuple comparison.

    Do this instead

    Build a new list shaped as (count, word) before calling sorted().

  • Common mistake

    Expecting a dictionary to be rearranged in place by sorted()

    What it looks like

    Treating sorted_pairs as though counts itself had been sorted

    Why it fails

    The technique constructs a list of tuples for sorting; the original dictionary remains the word-to-count mapping.

    Do this instead

    Store and use the sorted list of (value, key) tuples.

Practice the Transformation

EASY

A dictionary contains {'sun': 3, 'rain': 1, 'wind': 3}. Construct the list of (value, key) tuples and predict the result of sorted() before checking your work.

Hints
  • Loop through the dictionary's key and value together.
  • Append the value first and the key second.
  • Compare the counts first; compare the words only when counts tie.
Worked example

Checking the Practice Result

Sort the reconstructed entries for {'sun': 3, 'rain': 1, 'wind': 3}.

  1. 1
    Reconstruct

    The dictionary entries become (3, 'sun'), (1, 'rain'), and (3, 'wind').

  2. 2
    Compare first elements

    The tuple beginning with 1 comes before the tuples beginning with 3.

  3. 3
    Resolve the tie

    The two tuples beginning with 3 are compared by their second elements, rain-related entries being compared alphabetically as words; between sun and wind, sun comes first.

Result

[(1, 'rain'), (3, 'sun'), (3, 'wind')]

Key Takeaways

  1. 1
    urlopen() returns a file-like object that can be iterated over line by line.
  2. 2
    Web lines arrive as bytes, so decode them before splitting into words.
  3. 3
    Use counts[word] = counts.get(word, 0) + 1 to initialize and increment word frequencies.
  4. 4
    A dictionary entry normally has the shape (key, value); rebuild it as (value, key) when the value should control sorting.
  5. 5
    Tuple sorting compares the first element first and uses later elements to resolve ties.

At a glance

  • Retrieve remote text as a file-like response, decode each byte line, and process its words.
  • Maintain cumulative word frequencies with counts.get(word, 0) + 1.
  • Trace the dictionary after each update to locate counting errors.
  • Reconstruct entries as (value, key) so the desired value becomes the first tuple comparison.
  • When values tie, the tuple's later element determines the order.

Related concepts

More concepts starting with S