Last active
October 31, 2022 17:40
-
-
Save cadu-leite/db4e082b9ac0ea290f57956bb0518d8f to your computer and use it in GitHub Desktop.
Nested Dictionary values through dot path
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
get key values based on dot path | |
based on dict or json | |
d = { | |
'd1': 1, | |
'd2': { | |
'a': 2, 'b': 3, }, | |
'd3': { | |
'a': 4, 'b': { | |
'a': 5, 'b': 6, } | |
} | |
} | |
+----------+-----------------+ | |
| dot Path | Expected Output | | |
|==========+=================| | |
| "d1" | 1 | | |
|----------+-----------------| | |
| "d2.b" | 3 | | |
|----------+-----------------| | |
| "d3.b.b" | 6 | | |
|----------+-----------------| | |
| "d5" | None | | |
|----------+-----------------| | |
| "d1.b.b" | None | | |
+----------+-----------------+ | |
$> python3 get_key_dot_path.py | |
Using reduce | |
=1 | |
=3 | |
=6 | |
=None | |
=None | |
in 6.604194641113281e-05 seconds | |
Using for loop | |
=1 | |
=3 | |
=6 | |
=None | |
=None | |
in 4.6253204345703125e-05 seconds | |
Using recursion | |
=1 | |
=3 | |
=6 | |
=None | |
=None | |
in 5.7220458984375e-05 seconds | |
CProfile | |
======== | |
Using reduce | |
------------ | |
15001 function calls in 0.010 seconds | |
Random listing order was used | |
ncalls tottime percall cumtime percall filename:lineno(function) | |
5000 0.002 0.000 0.002 0.000 {method 'split' of 'str' objects} | |
5000 0.003 0.000 0.003 0.000 {built-in method _functools.reduce} | |
5000 0.005 0.000 0.010 0.000 get_key_dot_path.py:137(find_dot_path_reduce) | |
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} | |
Using for loop | |
-------------- | |
23001 function calls in 0.011 seconds | |
Random listing order was used | |
ncalls tottime percall cumtime percall filename:lineno(function) | |
8000 0.002 0.000 0.002 0.000 {method 'get' of 'dict' objects} | |
5000 0.002 0.000 0.002 0.000 {method 'split' of 'str' objects} | |
5000 0.001 0.000 0.001 0.000 {built-in method builtins.hasattr} | |
5000 0.006 0.000 0.011 0.000 get_key_dot_path.py:149(find_dot_path_for) | |
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} | |
Using recursion | |
---------------- | |
61001 function calls (57001 primitive calls) in 0.023 seconds | |
Random listing order was used | |
ncalls tottime percall cumtime percall filename:lineno(function) | |
9000 0.002 0.000 0.002 0.000 {method 'pop' of 'list' objects} | |
5000 0.001 0.000 0.001 0.000 {method 'reverse' of 'list' objects} | |
12000 0.002 0.000 0.002 0.000 {method 'get' of 'dict' objects} | |
5000 0.002 0.000 0.002 0.000 {method 'split' of 'str' objects} | |
9000 0.001 0.000 0.001 0.000 {built-in method builtins.hasattr} | |
7000 0.001 0.000 0.001 0.000 {built-in method builtins.len} | |
9000/5000 0.009 0.000 0.015 0.000 get_key_dot_path.py:121(get_key) | |
5000 0.006 0.000 0.023 0.000 get_key_dot_path.py:117(find_dot_path) | |
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} | |
''' | |
import functools | |
import operator | |
import time | |
# using recursion | |
def find_dot_path(dct, s): | |
keys_list = s.split('.') | |
keys_list.reverse() | |
def get_key(dct, keys): | |
key = keys.pop() | |
if not hasattr(dct, 'get'): | |
return None | |
value = dct.get(key, None) | |
if value is None or len(keys_list) == 0: | |
return value | |
else: | |
return get_key(dct.get(key), keys_list) | |
return get_key(dct, keys_list) | |
# using reduce | |
def find_dot_path_reduce(dct, path): | |
try: | |
return functools.reduce(operator.getitem, path.split('.'), dct) | |
except TypeError: | |
# case value <> subscriptable type | |
return None | |
except KeyError: | |
# case Key not Exists | |
return None | |
# using a loop | |
def find_dot_path_for(dct, path): | |
if hasattr(dct, '__getitem__'): | |
keys = path.split('.') | |
for key in keys: | |
try: | |
dct = dct.get(key, None) | |
except AttributeError: | |
return None | |
return dct | |
import cProfile | |
import pstats | |
if __name__ == '__main__': | |
d = { | |
'd1': 1, | |
'd2': { | |
'a': 2, 'b': 3, }, | |
'd3': { | |
'a': 4, 'b': { | |
'a': 5, 'b': 6, } | |
} | |
} | |
print(f'\nUsing reduce') | |
start_time = time.time() | |
print(f'={find_dot_path_reduce(d, "d1")}') | |
print(f'={find_dot_path_reduce(d, "d2.b")}') | |
print(f'={find_dot_path_reduce(d, "d3.b.b")}') | |
print(f'={find_dot_path_reduce(d, "d5")}') | |
print(f'={find_dot_path_reduce(d, "d1.b.b")}') | |
print(f'in {time.time() - start_time} seconds') | |
print(f'\nUsing for loop') | |
start_time = time.time() | |
print(f'={find_dot_path_for(d, "d1")}') | |
print(f'={find_dot_path_for(d, "d2.b")}') | |
print(f'={find_dot_path_for(d, "d3.b.b")}') | |
print(f'={find_dot_path_for(d, "d5")}') | |
print(f'={find_dot_path_for(d, "d1.b.b")}') | |
print(f'in {time.time() - start_time} seconds') | |
print(f'\nUsing recursion') | |
start_time = time.time() | |
print(f'={find_dot_path(d, "d1")}') | |
print(f'={find_dot_path(d, "d2.b")}') | |
print(f'={find_dot_path(d, "d3.b.b")}') | |
print(f'={find_dot_path(d, "d5")}') | |
print(f'={find_dot_path(d, "d1.b.b")}') | |
print(f'in {time.time() - start_time} seconds') | |
print(f'\nCProfile\n========') | |
print(f'\nUsing reduce\n-------------') | |
pr = cProfile.Profile() | |
pr.enable() | |
for k in range(1000): | |
find_dot_path_reduce(d, "d1") | |
find_dot_path_reduce(d, "d2.b") | |
find_dot_path_reduce(d, "d3.b.b") | |
find_dot_path_reduce(d, "d5") | |
find_dot_path_reduce(d, "d1.b.b") | |
pr.disable() | |
ps = pstats.Stats(pr).print_stats() | |
print(f'\nUsing for loop\n---------------') | |
pr = cProfile.Profile() | |
pr.enable() | |
for k in range(1000): | |
find_dot_path_for(d, "d1") | |
find_dot_path_for(d, "d2.b") | |
find_dot_path_for(d, "d3.b.b") | |
find_dot_path_for(d, "d5") | |
find_dot_path_for(d, "d1.b.b") | |
pr.disable() | |
ps = pstats.Stats(pr).print_stats() | |
print(f'\nUsing recursion\n----------------') | |
pr = cProfile.Profile() | |
pr.enable() | |
for k in range(1000): | |
find_dot_path(d, "d1") | |
find_dot_path(d, "d2.b") | |
find_dot_path(d, "d3.b.b") | |
find_dot_path(d, "d5") | |
find_dot_path(d, "d1.b.b") | |
pr.disable() | |
ps = pstats.Stats(pr).print_stats() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment