Concepts / Iterating Over Strings

Iterating Over Strings

Dictionaries outperform lists and variables for character counting because they only allocate space for characters that actually appear, not for every possible character.

  • Programming

Counting What Appears

Suppose you need to analyze a word and determine how often each character appears. For the word brontosaurus, the letters r, o, s, and u each appear twice, while b, n, t, and a appear once. This kind of frequency count records how often each item occurs.

A histogram is a collection of counters. In character counting, the dictionary containing each character and its frequency is the histogram, even when no graph is drawn.

The counting problem has three common strategies: use separate variables, use a list, or use a dictionary. All three can represent frequencies, but they differ in how much information must be known before processing begins and how much storage is reserved.

Three Storage Strategies

ApproachHow counts are storedMain limitation
Separate variablesOne variable for each possible characterThe possible characters must be decided in advance
ListA fixed position represents each possible characterThe possible character range and its numeric codes must be known
DictionaryA key represents a character and its value stores the countThe dictionary must be updated as characters are encountered

Separate variables and a fixed-size list require planning. For example, using 26 variables or a 26-element list reserves room for every letter of the alphabet, whether or not every letter appears. The list approach can work when the input is restricted to English letters and characters are converted to indices with ord(), but it depends on that known range.

A dictionary takes a different approach. It creates an entry only when a character actually appears. This means the dictionary does not require you to decide in advance exactly which characters the input will contain.

Variables26 countersList26 positionsDictionaryobserved characters
How do variables, lists, and dictionaries differ in the way they reserve storage for character counts?
acounter26 positionsletter rangeb1bcounterr2other letterscounterso2
What storage is represented when the input contains only some of the possible characters?

The Dictionary Decision

The dictionary algorithm uses one decision for every character. If the character is not already a key in the dictionary, add it with a count of 1. If it is already present, increase its existing count by 1. The pattern is if c not in d: initialize; otherwise: increment.

text = "brontosaurus" d = {} for c in text: if c not in d: d[c] = 1 else: d[c] = d[c] + 1 print(d)

Tracing Each Character

The word brontosaurus contains 12 characters but only 8 unique letters. The dictionary changes whenever the loop reads a character for the first time, and it changes an existing value whenever a repeated character is encountered.

StepCharacter readDictionary after the step
1b{'b': 1}
2r{'b': 1, 'r': 1}
3o{'b': 1, 'r': 1, 'o': 1}
4n{'b': 1, 'r': 1, 'o': 1, 'n': 1}
5t{'b': 1, 'r': 1, 'o': 1, 'n': 1, 't': 1}
6o{'b': 1, 'r': 1, 'o': 2, 'n': 1, 't': 1}
7s{'b': 1, 'r': 1, 'o': 2, 'n': 1, 't': 1, 's': 1}
8a{'b': 1, 'r': 1, 'o': 2, 'n': 1, 't': 1, 's': 1, 'a': 1}
9u{'b': 1, 'r': 1, 'o': 2, 'n': 1, 't': 1, 's': 1, 'a': 1, 'u': 1}
10r{'b': 1, 'r': 2, 'o': 2, 'n': 1, 't': 1, 's': 1, 'a': 1, 'u': 1}
11u{'b': 1, 'r': 2, 'o': 2, 'n': 1, 't': 1, 's': 1, 'a': 1, 'u': 2}
12s{'b': 1, 'r': 2, 'o': 2, 'n': 1, 't': 1, 's': 2, 'a': 1, 'u': 2}

Each new character creates a key with value 1; each repeated character increases an existing value.

brontosaurus{}start{b: 1}b{b: 1, r: 1}r{b: 1, r: 1, o: 1}o{b: 1, r: 1, o: 1, n:1}n{b: 1, r: 1, o: 1, n:1, t: 1}t{b: 1, r: 1, o: 2, n:1, t: 1}o{b: 1, r: 1, o: 2, n:1, t: 1, s: 1}s{b: 1, r: 1, o: 2, n:1, t: 1, s: 1, a: 1}a{b: 1, r: 1, o: 2, n:1, t: 1, s: 1, a: 1,u: 1}u{b: 1, r: 2, o: 2, n:1, t: 1, s: 1, a: 1,u: 1}r{b: 1, r: 2, o: 2, n:1, t: 1, s: 1, a: 1,u: 2}u{b: 1, r: 2, o: 2, n:1, t: 1, s: 2, a: 1,u: 2}s
How does the dictionary state change after each character in brontosaurus is processed?

Why Dictionaries Adapt

The dictionary approach is useful when the possible input is not known ahead of time. A string might contain letters, digits, punctuation, or Unicode symbols. A fixed list would require you to know the full range of possible characters and their numeric codes, while a dictionary can create keys for the characters that actually arrive.

The same check-and-update pattern also extends beyond characters. The source describes the pattern as useful for frequency tables, tallies, and histograms, and notes that it can be adapted to counting words or analyzing text in multiple languages.

observedobservedobservedobservedinputA7!λA171!1λ1
What keys and counts exist when the input contains different kinds of characters?

Choose a dictionary when the input character set may vary, when only characters that appear should receive entries, or when you want the same counting pattern to transfer to other kinds of frequency data.

Common Counting Mistakes

  • Incrementing a dictionary key before creating it.

    A new character does not yet have a counter in the dictionary.

    Fix: Check whether c is in d. Create d[c] with value 1 when it is new; otherwise increment the existing value.

  • Treating every character as a repeated character.

    The first occurrence of a character needs initialization rather than an update.

    Fix: Use the if c not in d decision to distinguish first occurrences from later occurrences.

  • Choosing a fixed list without considering the input.

    The list approach requires the possible character range and numeric codes to be known.

    Fix: Use a dictionary when the input can contain characters outside a known fixed range.

  • Assuming the dictionary must contain every possible character.

    The dictionary approach creates entries only for characters that actually appear.

    Fix: Interpret the dictionary as a record of observed characters and their frequencies.

Practice the Trace

What do you think happens?

What will the dictionary contain after this loop processes the string "abaca"?

Reveal answer

Answer: {'a': 3, 'b': 1, 'c': 1}

The first a creates a count of 1, b creates a count of 1, the second a increases a to 2, c creates a count of 1, and the final a increases a to 3.

EASY

Trace the string "level" one character at a time. Write the dictionary after each character is processed, then identify which keys have a final count greater than 1.

Hints
  • Begin with an empty dictionary.
  • Create a key with value 1 the first time a character appears.
  • Increase the value when the character appears again.

Key Takeaways

  1. Character frequencies can be stored with separate variables, a list, or a dictionary.
  2. Variables and fixed lists require the possible character set to be decided in advance.
  3. The dictionary pattern checks whether a character is already present, then initializes or increments its count.
  4. A dictionary stores only characters that actually appear, so it adapts to letters, digits, punctuation, and Unicode symbols.
  5. The dictionary is a histogram: its keys are the observed items and its values are their frequencies.

Key Takeaways

  • A frequency counter records how often each character appears.
  • The key dictionary decision is whether the current character is new or already present.
  • New characters receive a count of 1, while repeated characters increase their existing count.
  • Dictionaries are usually the most adaptable choice because they store only observed characters and do not require a predefined character range.