Python3 search by value in nested dictionaries and lists then get other nearest keys/values pair

Viewed 107

I have a dictionary like this:

{'targets': [{'hosts': ['google.fr'],
              'tags': {'country': 'fr',
                       'server': 'french'}},
             {'hosts': ['amazon.es'],
              'tags': {'country': 'es',
                       'server': 'spain'}},
             {'hosts': ['facebook.de'],
              'tags': {'country': 'de',
                       'server': 'germany'}},
             {'hosts': ['server1.fr', 'server2.fr', 'server3.fr'],
              'tags': {'country': 'fr',
                       'server': 'french'}}]}

Basically a dictionary with nested lists, dictionaries, and strings, of arbitrary depth.

What is the best way of traversing this to extract the nearest key 'tags' where 'host' equal to my search ?

My attempt:

#host = 'google.fr' or 'amazon.es' or 'facebook.de' or 'server1.fr' or 'server2.fr' or 'server3.fr'

for [idx, val] in enumerate(targets['targets']):
    h = targets['targets'][idx]['hosts']
    for i in h:
        if i == host and 'tags' in targets['targets'][idx]: # if equal to host and 'tags' key exist
            t = targets['targets'][idx]['tags']

            tags = ','.join("{!s}={!r}".format(key,val) for (key,val) in t.items())
            tags = tags.replace("'", "")

            pprint ("host: '{}'".format(host))
            pprint("tags: '{}'".format(tags))

Result:

"host: 'google.fr'"
"tags: 'country=fr,server=french'"
"host: 'amazon.es'"
"tags: 'country=es,server=spain'"
"host: 'facebook.de'"
"host: 'server1.fr'"
"tags: 'country=fr,server=french'"
"host: 'server2.fr'"
"tags: 'country=fr,server=french'"
"host: 'server3.fr'"
"tags: 'country=fr,server=french'"

I'm not a python expert and I'm sure it's possible to have better performance.

Benchmark:

        wwii        azro

real    0m0,087s    0m0,057s
user    0m0,071s    0m0,027s
sys     0m0,015s    0m0,028s

real    0m0,078s    0m0,067s
user    0m0,068s    0m0,056s
sys     0m0,009s    0m0,010s

real    0m0,074s    0m0,058s
user    0m0,062s    0m0,024s
sys     0m0,011s    0m0,034s

Looks like azro's performing better :)

2 Answers

You can remove the indexed-iteration and look for host inclusion rather than equality

host = "google.fr"
for target in targets['targets']:
    if host in target['hosts'] and 'tags' in target:
        tags = ','.join("{!s}={!r}".format(key, val) for (key, val) in target['tags'].items())
        tags = tags.replace("'", "")
        print("host: '{}'".format(host))
        print("tags: '{}'".format(tags))

Assuming the dictionary is d:

import operator
hosts = operator.itemgetter('hosts')
tags = operator.itemgetter('tags')
fmt = 'country={country},server={server}'
space = d['targets']

term = 'google.fr'
# filter
items = (item for item in space if term in hosts(item))
for item in items:
    print(f'host:{term}')
    print(fmt.format(**tags(item)))
    # or
    print(','.join(f'{key}={value}' for key,value in tags(item).items()))
Related