dictionary
programming
tutorial
Python
data structures

How to create key or append an element to key?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

When building a dictionary where values are lists, you need to handle the case where a key does not yet exist. Appending to a missing key raises KeyError. The cleanest solutions are collections.defaultdict(list), which auto-creates empty lists for new keys, and dict.setdefault(key, []).append(value), which initializes and appends in one call. Both eliminate the manual "check if key exists, create list if not, then append" pattern.

The Problem

python
data = {}
data["fruits"].append("apple")
# KeyError: 'fruits'

The key "fruits" does not exist, so there is no list to append to.

python
1from collections import defaultdict
2
3data = defaultdict(list)
4
5data["fruits"].append("apple")
6data["fruits"].append("banana")
7data["vegetables"].append("carrot")
8data["fruits"].append("cherry")
9
10print(dict(data))
11# {'fruits': ['apple', 'banana', 'cherry'], 'vegetables': ['carrot']}

When you access data["fruits"] and it does not exist, defaultdict calls list() to create an empty list, stores it, and returns it. The .append() then works normally.

Method 2: dict.setdefault()

python
1data = {}
2
3data.setdefault("fruits", []).append("apple")
4data.setdefault("fruits", []).append("banana")
5data.setdefault("vegetables", []).append("carrot")
6
7print(data)
8# {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']}

setdefault(key, default) returns the existing value if the key exists, or sets and returns the default if it does not. Chaining .append() adds to the returned list.

Method 3: Manual Check

python
1data = {}
2key = "fruits"
3value = "apple"
4
5if key not in data:
6    data[key] = []
7data[key].append(value)

Explicit but verbose. Use defaultdict or setdefault instead.

Method 4: Try/Except

python
1data = {}
2
3try:
4    data["fruits"].append("apple")
5except KeyError:
6    data["fruits"] = ["apple"]

This follows Python's "easier to ask forgiveness than permission" (EAFP) principle, but it is clumsier than setdefault for this use case.

Comparison

python
1from collections import defaultdict
2
3items = [("fruit", "apple"), ("fruit", "banana"), ("veggie", "carrot"),
4         ("fruit", "cherry"), ("veggie", "pea")]
5
6# defaultdict - cleanest
7d1 = defaultdict(list)
8for k, v in items:
9    d1[k].append(v)
10
11# setdefault - no import needed
12d2 = {}
13for k, v in items:
14    d2.setdefault(k, []).append(v)
15
16# Manual check - most explicit
17d3 = {}
18for k, v in items:
19    if k not in d3:
20        d3[k] = []
21    d3[k].append(v)
22
23# All produce: {'fruit': ['apple', 'banana', 'cherry'], 'veggie': ['carrot', 'pea']}

Creating Key or Incrementing a Counter

The same pattern applies to counters:

python
1from collections import defaultdict
2
3# Counting occurrences
4counter = defaultdict(int)
5words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
6
7for word in words:
8    counter[word] += 1
9
10print(dict(counter))
11# {'apple': 3, 'banana': 2, 'cherry': 1}
12
13# Or use collections.Counter directly
14from collections import Counter
15counter = Counter(words)

Creating Key or Adding to a Set

python
1from collections import defaultdict
2
3# Unique values per key
4data = defaultdict(set)
5data["fruits"].add("apple")
6data["fruits"].add("banana")
7data["fruits"].add("apple")  # Duplicate - ignored
8
9print(dict(data))
10# {'fruits': {'apple', 'banana'}}

Real-World Example: Grouping Records

python
1from collections import defaultdict
2
3students = [
4    {"name": "Alice", "grade": "A"},
5    {"name": "Bob", "grade": "B"},
6    {"name": "Charlie", "grade": "A"},
7    {"name": "Diana", "grade": "C"},
8    {"name": "Eve", "grade": "B"},
9]
10
11by_grade = defaultdict(list)
12for student in students:
13    by_grade[student["grade"]].append(student["name"])
14
15print(dict(by_grade))
16# {'A': ['Alice', 'Charlie'], 'B': ['Bob', 'Eve'], 'C': ['Diana']}

Nested defaultdict

python
1from collections import defaultdict
2
3# Two-level grouping
4sales = defaultdict(lambda: defaultdict(list))
5
6sales["2025"]["Q1"].append(1000)
7sales["2025"]["Q1"].append(1200)
8sales["2025"]["Q2"].append(1500)
9sales["2024"]["Q4"].append(900)
10
11print(sales["2025"]["Q1"])  # [1000, 1200]

Other Languages

JavaScript

javascript
1const data = {};
2const key = "fruits";
3
4// Using logical OR
5(data[key] = data[key] || []).push("apple");
6
7// Using nullish coalescing
8(data[key] ??= []).push("apple");

Java

java
1Map<String, List<String>> data = new HashMap<>();
2
3// computeIfAbsent - creates list if key is absent
4data.computeIfAbsent("fruits", k -> new ArrayList<>()).add("apple");
5data.computeIfAbsent("fruits", k -> new ArrayList<>()).add("banana");

Ruby

ruby
data = Hash.new { |h, k| h[k] = [] }
data["fruits"] << "apple"
data["fruits"] << "banana"

Common Pitfalls

  • dict.fromkeys(keys, []) shares one list: All keys point to the same list object. Use {k: [] for k in keys} instead to create independent lists.
  • **defaultdict(list) vs defaultdict([])**: defaultdicttakes a callable.listis callable (creates[]). []is not callable - raisesTypeError`.
  • Accessing defaultdict creates keys: if key in d is safe, but d[key] creates the key with a default value even if you only wanted to check. Use key in d for existence checks.
  • JSON serialization: json.dumps() works with defaultdict, but the default factory is lost. Convert to dict() first if round-tripping matters.
  • Thread safety: Neither defaultdict nor setdefault is atomic. In multi-threaded code, use threading.Lock or concurrent.futures.

Summary

  • Use defaultdict(list) for the cleanest auto-creating dictionary of lists
  • Use dict.setdefault(key, []).append(value) when you want a plain dict with no imports
  • Never use dict.fromkeys(keys, []) - all keys share the same list object
  • The pattern works with set, int, or any callable as the default factory
  • In JavaScript use ??=, in Java use computeIfAbsent, in Ruby use Hash.new with a block

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.