Dictionary Merger
Problem Statement
Merge and manipulate dictionaries.
Background
Python dictionaries support:
- Access: d[key] or d.get(key, default)
- Update: d[key] = value or d.update(other_dict)
- Keys/Values: d.keys(), d.values(), d.items()
- Merging (Python 3.9+): d1 | d2
Your Task
Write a function merge_dicts(dict1, dict2) that merges two dictionaries.
Merge Rules
- If a key exists in both, sum the values (if numeric) or concatenate (if strings)
- Keep unique keys from both dictionaries
Output Format
Return the merged dictionary.
Example:
dict1 = {"a": 1, "b": 2, "c": "hello"}, dict2 = {"b": 3, "d": 4, "c": " world"}{'a': 1, 'b': 5, 'c': 'hello world', 'd': 4}'a' only in dict1, 'b' in both (2+3=5), 'c' in both (concat), 'd' only in dict2
Constraints:
- Values are either numbers (int/float) or strings
- For numbers, sum them; for strings, concatenate
Background Knowledge
Python dictionaries are mutable, unordered collections of key-value pairs where keys must be immutable (e.g., strings, integers) and unique. Key operations include accessing values via d[key] (raises KeyError if missing) or d.get(key, default), iterating over d.keys(), d.values(), or d.items(), and updating with d[key] = value or d.update(other_dict). Since Python 3.9, the merge operator | creates a new dictionary from two inputs, preserving the first dictionary's order and overwriting duplicates with the second's values, but it does not handle custom merging like summing numbers or concatenating strings.
This problem requires conditional merging based on value types: numeric values (e.g., int, float) should sum, while strings should concatenate without spaces unless specified. Understanding type checking with isinstance(value, (int, float)) or type(value) is essential, as is handling mixed types safely to avoid errors. Dictionaries preserve insertion order (Python 3.7+), so the merged result should reflect keys from dict1 first, then new keys from dict2.
Algorithm/Approach
Use an iterative merge with type-aware combination, starting with a copy of the first dictionary and processing the second's keys. This is a common pattern for custom dictionary operations, similar to reducing over key-value pairs while applying a merge function:
- Copy dict1 to preserve the original.
- For each key in dict2, check if it exists in the copy:
- If yes, combine values based on type (sum for numbers, concatenate for strings).
- If no, add directly.
- Return the modified copy.
This avoids Python's built-in | or update() for fine-grained control. Time: O(n + m) where n and m are dictionary sizes; Space: O(n + m) for the result.
Step-by-Step Strategy
- Initialize result: Create result = dict1.copy() to start with all keys/values from the first dictionary.
- Iterate over dict2: Use for key, value in dict2.items(): to process each pair.
- Check key existence: Use if key in result: to detect overlaps.
- Type-safe merge:
- Retrieve existing value: existing = result[key].
- Check if both are numbers: isinstance(existing, (int, float)) and isinstance(value, (int, float)) → result[key] = existing + value.
- Check if both are strings: isinstance(existing, str) and isinstance(value, str) → result[key] = existing + value.
- Otherwise, assign dict2's value (or handle per rules).
- Add new keys: If key not in result, set result[key] = value.
- Return result: Ensures all unique keys are included.
Test with sample: "a" kept as-is, "b": 2+3=5, "c": "hello"+" world", "d" added.
Common Pitfalls
- Type mismatches: Summing string + int raises TypeError; always check isinstance before operations.
- Mutable defaults: Avoid dict.get(key, []) for lists if mutating, but here use explicit checks.
- Non-string/number values: Problem assumes numbers/strings; unhandled types (e.g., lists) may error—consider else: result[key] = value as fallback.
- Order preservation: dict1.copy() + sequential adds maintain order; using {**dict1, **dict2} loses custom logic.
- Empty dictionaries: Edge case merge_dicts({}, {}) → {} works naturally.
- Floating-point precision: 1.0 + 2 → 3.0; use float checks if needed.
Time & Space Complexity
- Time: O(n + m), single pass over both dictionaries (n = len(dict1), m = len(dict2)); in checks are O(1) average for hash tables.
- Space: O(n + m) for the result dictionary; copy() uses O(n), no extra asymptotic space.
Practice by tracing the sample input manually, then implement and test edge cases like {"a": "x", "a": 1} (string wins?) or empty inputs.