Dictionary (dict
) là cấu trúc dữ liệu cực kỳ phổ biến trong Python vì khả năng ánh xạ nhanh giữa key → value. Trong thực tế mình thường dùng dict
để lưu config, hồ sơ người dùng, cache tạm,… và bài toán tìm kiếm xuất hiện ở mọi nơi: kiểm tra tồn tại key, lấy value an toàn, lọc theo điều kiện, hoặc tìm trong cấu trúc lồng nhau.
Bài viết này mình cùng bạn sẽ tìm hiểu chi tiết các phương pháp tìm kiếm trong dictionary, từ cơ bản đến nâng cao, thông qua những ví dụ minh họa và giải thích rõ ràng. Hy vọng rằng sau khi đọc xong, chúng ta sẽ nắm vững cách xử lý các tình huống tìm kiếm trong thực tế.
1. Cách dict
hoạt động và ý nghĩa khi tìm kiếm
Trước khi đi vào kỹ thuật, cần nhớ vài điểm nền tảng:
dict
dùng hash table: lookup theo key trung bình là O(1) (average-case).- Key phải hashable (immutable types:
str
,int
,tuple
nếu tuple các phần tử hashable). Mutable types nhưlist
không thể làm key. - Tìm value (không phải key) thường phải duyệt toàn bộ
dict
→ O(n).
Hiểu rõ điều này giúp lựa chọn đúng phương pháp: nếu bạn search theo key — dùng lookup trực tiếp; nếu bạn cần lọc theo value nhiều lần — cân nhắc indexing ngoài (ví dụ inverted index).
2. Tìm theo key: in
, []
, get()
và KeyError
Các thao tác phổ biến:
key in my_dict
— trảTrue/False
. Rất nhanh (O(1) average).my_dict[key]
— trả value hoặc KeyError nếu không tồn tại.my_dict.get(key, default)
— trả value hoặc default (không raise lỗi).
Ví dụ:
# Example dict
person = {"name": "Alice", "age": 30, "email": "alice@example.com"}
# 1) Check existence
if "name" in person:
print("Key exists")
# 2) Direct index (may raise KeyError)
try:
value = person["address"] # may raise KeyError
except KeyError:
print("Key 'address' not found")
# 3) Safe get
address = person.get("address", "Unknown")
print(address) # prints "Unknown"
Gợi ý best-practice: nếu bạn chỉ cần kiểm tra tồn tại → in
. Nếu bạn muốn lấy value và chấp nhận fallback → get()
.
3. Tìm value: values()
, in
, next()
và comprehension
Khi cần kiểm tra hoặc tìm theo value:
value in my_dict.values()
— đơn giản, O(n).next((k for k, v in my_dict.items() if v == target), None)
— trả key đầu tiên khớp.- Comprehension cho nhiều kết quả:
[k for k, v in my_dict.items() if predicate(v)]
.
Ví dụ:
# Check if a value exists
if "alice@example.com" in person.values():
print("Email present")
# Find the first key with given value
found_key = next((k for k, v in person.items() if v == "Alice"), None)
print(found_key) # "name" or None
# Find all keys with numeric values
numeric_keys = [k for k, v in person.items() if isinstance(v, (int, float))]
print(numeric_keys) # ['age']
Lưu ý: tìm value luôn O(n); nếu bạn cần tìm theo value rất thường xuyên, hãy xây dựng inverted index (map value → list of keys) để tra cứu O(1).
4. Tìm với nhiều điều kiện và filter nâng cao
Thực tế thường cần lọc theo nhiều điều kiện (ví dụ: người dùng có age > 25
và city == "Hanoi"
). Dùng comprehension hoặc filter
kết hợp predicate:
users = {
"u1": {"name": "A", "age": 28, "city": "Hanoi"},
"u2": {"name": "B", "age": 22, "city": "Hanoi"},
"u3": {"name": "C", "age": 30, "city": "Saigon"},
}
# Find user ids with age > 25 and city Hanoi
result = [
user_id
for user_id, info in users.items()
if info.get("age", 0) > 25 and info.get("city") == "Hanoi"
]
print(result) # ['u1']
Đây là pattern phổ biến trong filter data trên dict lưu object/simple dict làm value.
5. Tìm trong dict
lồng nhau — recursive search và trả về đường dẫn (path)
Khi cấu trúc lồng nhau (config, JSON), thường muốn tìm tất cả vị trí chứa key hoặc value. Mình hay dùng hàm đệ quy trả về danh sách đường dẫn (list of keys path).
Ví dụ hàm tìm key bất kỳ:
def find_key_paths(d, target_key, current_path=None):
"""Recursively search for target_key in nested dicts.
Returns list of paths (each path is a list of keys).
"""
if current_path is None:
current_path = []
paths = []
if isinstance(d, dict):
for k, v in d.items():
new_path = current_path + [k]
if k == target_key:
paths.append(new_path)
if isinstance(v, dict):
paths.extend(find_key_paths(v, target_key, new_path))
elif isinstance(v, list):
# search inside dicts in lists
for idx, item in enumerate(v):
if isinstance(item, dict):
paths.extend(find_key_paths(item, target_key, new_path + [idx]))
return paths
# Example usage
config = {
"db": {"host": "localhost", "port": 5432},
"services": {"auth": {"host": "auth.local", "port": 8000}},
}
print(find_key_paths(config, "host"))
# e.g. [['db', 'host'], ['services', 'auth', 'host']]
Hàm trên trả đường dẫn để bạn có thể cập nhật/ghi nhận vị trí chính xác.
6. Viết hàm tìm kiếm tổng quát (robust search API)
Mình thường đóng gói thành hàm tiện dụng có option: match_key
, match_value
, predicate
, first_only
, return_path
.
def search_dict(dictionary, *, match_key=None, match_value=None, predicate=None, first_only=False, return_path=False):
"""
Search dictionary for entries that match match_key/match_value or pass predicate.
- match_key: exact key match
- match_value: exact value match
- predicate: callable(k, v) -> bool
- first_only: stop at first match
- return_path: if True, returns (path, key, value) where path is list of keys
"""
results = []
def _walk(d, path):
if isinstance(d, dict):
for k, v in d.items():
full_path = path + [k]
ok = False
if match_key is not None and k == match_key:
ok = True
if match_value is not None and v == match_value:
ok = True
if predicate is not None and predicate(k, v):
ok = True
if ok:
if return_path:
results.append((full_path, k, v))
else:
results.append((k, v))
if first_only:
return True
if isinstance(v, dict):
if _walk(v, full_path):
return True
elif isinstance(v, list):
for idx, item in enumerate(v):
if isinstance(item, dict):
if _walk(item, full_path + [idx]):
return True
return False
_walk(dictionary, [])
return results
# Example: find first key 'host'
print(search_dict(config, match_key="host", first_only=True, return_path=True))
Hàm này dùng đệ quy, hỗ trợ nhiều tuỳ chọn, phù hợp khi cần reuse trên nhiều project.
7. Hiệu năng, độ phức tạp và best practices
- Key lookup: O(1) average. Value lookup / iteration: O(n).
- Hash collisions: trong trường hợp cực hiếm (vì hash collision), worst-case có thể thoát O(n), nhưng Python dicts tối ưu tốt.
- Tránh dùng mutable types làm key. Nếu cần composite key, dùng
tuple
hoặcfrozenset
(tuỳ mục đích). - Không sửa
dict
khi đang iterate (sẽ raiseRuntimeError: dictionary changed size during iteration
). Nếu cần xóa, thu thập keys cần xóa rồi lặp qua bản sao:for k in list(d.keys()): ...
- Nếu search theo value rất nhiều, tạo inverted index:
value_to_keys = defaultdict(list)
rồi populate. Tradeoff: tăng memory để giảm time. - Normalization: khi so sánh string (case-insensitive), chuẩn hóa (
.lower().strip()
) trước khi so sánh.
8. Những lỗi thường gặp & cách debug nhanh
- KeyError: do dùng
dict[key]
cho key không tồn tại → dùngget()
hoặctry/except
. - So sánh sai kiểu:
1
(int) khác"1"
(str). Luôn kiểm tra kiểu khi so sánh. - Mutable key:
TypeError
nếu cố dùng list làm key. - Sửa dict khi iterate →
RuntimeError
. - Không hiểu
values()
là view —my_dict.values()
trả một view object (không phải list). Nếu muốn snapshot, dùnglist(my_dict.values())
.
Debug tips: dùng print(type(value))
, assert
nhỏ để kiểm tra invariant, và viết test unit cho hàm tìm kiếm.
9. Kết luận
Qua bài viết này, mình đã cùng bạn đi từ những cách cơ bản như kiểm tra sự tồn tại của key với in
, truy xuất an toàn bằng get()
, cho đến những kỹ thuật nâng cao hơn như lọc theo điều kiện, tìm trong dictionary lồng nhau bằng đệ quy hay thậm chí xây dựng hàm tìm kiếm tổng quát.
Điều quan trọng nhất cần nhớ là:
- Tra cứu theo key trong dictionary rất nhanh và an toàn nếu bạn dùng đúng công cụ.
- Tìm theo value thì tốn chi phí hơn, vì vậy hãy cân nhắc đến việc tạo index khi dữ liệu lớn.
- Với những nhu cầu đặc thù, bạn hoàn toàn có thể tự viết hàm tìm kiếm tùy chỉnh để tiết kiệm công sức trong các lần sử dụng sau.
Hy vọng sau khi đọc xong, bạn không chỉ biết cách “tìm cái mình muốn” trong dictionary, mà còn có tư duy chọn đúng phương pháp trong từng tình huống. Với mình, đây chính là sự khác biệt giữa viết code cho chạy được và viết code để dễ bảo trì, hiệu quả về lâu dài.
10. Tài liệu tham khảo (định dạng APA)
- Python Software Foundation. (2024). Built-in Types — dict. Python Documentation. Retrieved from https://docs.python.org/3/library/stdtypes.html#dict
- Ramalho, L. (2015). Fluent Python: Clear, Concise, and Effective Programming. O’Reilly Media.
- Beazley, D., & Jones, B. K. (2013). Python Cookbook (3rd ed.). O’Reilly Media.
- Real Python. (n.d.). Dictionaries in Python. Real Python. Retrieved from https://realpython.com/python-dicts/