What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
To select keys throughout a nested dictionary, walk each dictionary’s key-value pairs, decide whether each key should be kept, and recursively inspect values that are dictionaries. The key design choice is what happens to a parent branch when a selected key appears deeper inside it. The example below returns a new dictionary, retains ancestors of matching nested keys, and filters nested dictionaries even when their parent key matches.
A recursive selector with an explicit contract
Python dictionaries can hold arbitrary values, so recursion does not happen automatically. This function descends into dictionary values only; it does not traverse lists or tuples. It accepts an iterable of keys to match by exact membership, returns ordinary dictionaries, leaves non-dictionary values unchanged, and keeps a nonmatching parent branch when it contains selected keys below it. Empty branches with no selected key are omitted.
def select_keys(data, wanted):
"""Return selected keys and their matching nested dictionary branches.
Recurses into dict values only. Keeps a branch when it contains
a selected key at any depth. Does not mutate data.
"""
wanted = set(wanted)
result = {}
for key, value in data.items():
if isinstance(value, dict):
value = select_keys(value, wanted)
if key in wanted:
result[key] = value
elif isinstance(value, dict) and value:
result[key] = value
return result
record = {
"name": "Mina",
"profile": {
"email": "[email protected]",
"address": {"city": "Oslo", "postcode": "0150"},
},
"settings": {"theme": "dark"},
}
selected = select_keys(record, {"email", "city"})
print(selected)
# {'profile': {'email': '[email protected]',
# 'address': {'city': 'Oslo'}}}
In this result, profile and address remain even though neither is selected: each contains a selected descendant. The name and settings branches disappear because neither their keys nor any descendant keys match. Since a fresh dictionary is built at every visited dictionary, the input is not modified.
Why recurse before deciding what to keep?
The function first filters a dictionary-valued value, then evaluates the current key. That ordering means that if profile itself is in wanted, its nested dictionary is still filtered rather than copied wholesale. If matching a key should instead preserve its entire value untouched, the policy must change: test membership first and copy the value directly for matching keys. That simpler policy is appropriate when “select key” means “keep this key and everything under it,” rather than “filter matching keys at every level.”
Recommended Free Tools
#1 Best Overall
Choose the branch and value policies
Keep ancestors, or only matching keys?
The implementation above keeps ancestors of matches so selected descendants remain reachable in the output. An alternative is to retain only keys that match, without retaining nonmatching containers. That would make a nested match inaccessible unless the result’s shape were flattened or represented differently. For most nested-dictionary filters, preserving ancestor branches is the useful behavior.
The example drops empty dictionaries created by filtering, because it only retains a nonmatching dictionary-valued key when the filtered result is nonempty. If empty branches carry meaning in your data, remove the and value test:
elif isinstance(value, dict):
result[key] = value
With that change, a nonmatching branch may remain as {} even when it contains no selected key. Decide whether an empty object represents useful structure or noise in the particular data format.
Rank #2
Dictionary values versus lists and tuples
The function intentionally treats lists, tuples, sets, and other objects as opaque values. For example, a value like {"events": [{"email": "[email protected]"}]} remains unchanged if events matches, and is not searched if it does not. Descending through sequences requires a separate rule: should each nested dictionary be filtered, should the sequence type be preserved, and what should happen to elements that become empty? Do not add generic container recursion without answering those questions; different containers have different semantics.
Exact keys, predicates, and non-string keys
Here, wanted is converted to a set for efficient membership checks, and keys match by ordinary Python equality and hashing. Keys need not be strings: dictionaries can use other hashable key types. If your data uses heterogeneous keys, provide matching values of the appropriate types. A string "1" and integer 1 are distinct keys.
For a rule such as “keep every key beginning with user_,” use a predicate instead of a membership set:
def select_keys_where(data, keep_key):
result = {}
for key, value in data.items():
if isinstance(value, dict):
value = select_keys_where(value, keep_key)
if keep_key(key):
result[key] = value
elif isinstance(value, dict) and value:
result[key] = value
return result
result = select_keys_where(
{"account": {"user_id": 7, "email": "[email protected]", "prefs": {"user_theme": "dark"}}},
lambda key: isinstance(key, str) and key.startswith("user_"),
)
The isinstance guard matters when keys are not guaranteed to be strings; calling startswith on an integer would raise AttributeError.
Supporting Mapping implementations
Use dict when the function’s contract is deliberately limited to built-in dictionaries and their subclasses. Python documents dict as its standard mapping type, with keys that are hashable and values that may be arbitrary objects (Python 3.13 Built-in Types documentation). An isinstance(value, dict) check also accepts subclasses; it is broader than checking type(value) is dict (Python 3.13 Built-in Functions documentation).
If the input may include custom mapping objects, use the abstract interface in collections.abc:
from collections.abc import Mapping
def select_mapping_keys(data, wanted):
wanted = set(wanted)
result = {}
for key, value in data.items():
if isinstance(value, Mapping):
value = select_mapping_keys(value, wanted)
if key in wanted:
result[key] = value
elif isinstance(value, Mapping) and value:
result[key] = value
return result
Mapping describes a mapping interface with __getitem__, __iter__, and __len__, along with mixin operations such as items and get (Python 3.12.14 collections.abc documentation). This version accepts more mapping implementations, but still constructs ordinary dictionaries. If callers require output to retain custom mapping types, define how each type is rebuilt; there is no safe universal assumption that calling {} or the input type’s constructor preserves its behavior.
Run and test the function safely
- Decide the contract. Specify mapping types, which values are traversed, whether ancestors remain, whether empty branches remain, and whether selected parent values are themselves filtered.
- Use a small example with a match at multiple depths. Include a matching key whose value is another dictionary, a nonmatching branch, and a nested match under a nonmatching parent.
- Check that the source stays unchanged. The implementation above creates new dictionaries for the mappings it visits, but it does not deep-copy opaque values such as lists or custom objects.
- Check edge cases. Try an empty input, an empty selection set, and a key whose matching value is
{}. With the shown policy, an empty dictionary under a selected key is retained; a nonmatching empty branch is discarded.
source = {"outer": {"keep": 1, "drop": 2}, "drop": 3}
result = select_keys(source, {"keep"})
assert result == {"outer": {"keep": 1}}
assert source == {"outer": {"keep": 1, "drop": 2}, "drop": 3}
assert select_keys({}, {"keep"}) == {}
assert select_keys({"x": 1}, set()) == {}
Cycles, shared objects, and recursion limits
Nested JSON-like data is normally a tree: each child leads to a finite object that does not point back to an ancestor. General Python objects need not have that shape. A dictionary can contain itself, directly or indirectly, causing this straightforward recursive function to continue until Python raises RecursionError. Deeply nested input can reach the recursion limit even without a cycle.
For untrusted or arbitrary object graphs, choose an explicit policy. You can reject cyclic inputs, track object identities currently on the recursion path to detect cycles, or implement an iterative traversal with a stack. A global “already visited” set has a different effect: it can treat a shared dictionary referenced in two places as if the second occurrence were a cycle and omit or reuse it. If shared references must be preserved, the output policy needs to track source-to-output objects, not just visited identities.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteBest Value
Common mistakes and fixes
- Only filtering the root: a comprehension such as
{k: v for k, v in data.items() if k in wanted}never examines nested dictionaries. Call the recursive function on dictionary values. - Losing nested matches: keeping only matching keys drops the nonmatching ancestors needed to reach a nested selection. Retain nonempty filtered branches, as in the example.
- Keeping an entire selected subtree unintentionally: recurse into dictionary values before testing whether their parent key matches.
- Unexpected list contents: the dictionary-only contract leaves lists untouched. Add list traversal only after specifying element handling and output type preservation.
- Type errors with predicates: test key types before string operations or comparisons that assume strings.
- Mutating while iterating: modifying the same dictionary’s keys during iteration can cause errors and surprising behavior. Build a new result, or iterate over a stable copy if mutation is truly required.
- Custom mappings disappear as custom types: accepting
Mappingbroadens inputs but this implementation returnsdict. Add a deliberate reconstruction rule if type preservation is required. - Cycles or excessive depth: validate that inputs are finite trees, add cycle handling, or use an iterative traversal for deep structures.
Or skip the browser setup
Recursive dictionary selection is a local Python operation, so ScreenshotNeo is not a replacement for this function. For a separate developer task—capturing a web page as an image or PDF—ScreenshotNeo accepts a URL in one GET request:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
See the ScreenshotNeo API documentation for request options. It removes cookie/consent banners, newsletter popups and chat widgets before capture; bot checks, blank pages and failed loads are not billed. An MCP server lets AI agents use its screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Learn about ScreenshotNeo or sign up free.
Frequently asked questions
Does this function alter the original dictionary?
No. It builds new dictionaries for traversed mappings. Other objects held as values are passed through unchanged rather than copied.
Is there a built-in recursive dictionary key selector?
The cited Python documentation describes dictionaries, mappings, and related operations, but does not document a standard-library function dedicated to recursive key selection. Treat the code here as an application-specific recipe and define its contract for your data.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsWhat happens when wanted is empty?
No key matches, so the result is empty under the shown branch policy.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

