UFO ET IT

중첩 된 파이썬 사전 및 목록에서 모든 키 발생 찾기

ufoet 2020. 11. 7. 18:15
반응형

중첩 된 파이썬 사전 및 목록에서 모든 키 발생 찾기


다음과 같은 사전이 있습니다.

{ "id" : "abcde",
  "key1" : "blah",
  "key2" : "blah blah",
  "nestedlist" : [ 
    { "id" : "qwerty",
      "nestednestedlist" : [ 
        { "id" : "xyz",
          "keyA" : "blah blah blah" },
        { "id" : "fghi",
          "keyZ" : "blah blah blah" }],
      "anothernestednestedlist" : [ 
        { "id" : "asdf",
          "keyQ" : "blah blah" },
        { "id" : "yuiop",
          "keyW" : "blah" }] } ] } 

기본적으로 임의의 깊이의 중첩 된 목록, 사전 및 문자열이있는 사전입니다.

모든 "id"키의 값을 추출하기 위해 이것을 순회하는 가장 좋은 방법은 무엇입니까? "// id"와 같은 XPath 쿼리에 해당하는 것을 얻고 싶습니다. "id"의 값은 항상 문자열입니다.

따라서 내 예에서 필요한 출력은 기본적으로 다음과 같습니다.

["abcde", "qwerty", "xyz", "fghi", "asdf", "yuiop"]

순서는 중요하지 않습니다.


이 Q / A는 동일한 문제에 대해 여러 가지 다른 솔루션을 제공하기 때문에 매우 흥미로 웠습니다. 이 모든 기능을 가져와 복잡한 사전 객체로 테스트했습니다. 테스트에서 두 개의 함수를 가져와야했습니다. 왜냐하면 그들은 많은 실패 결과를 가져야했고, 거의 모든 데이터가 올 때마다 함수가 준비되어야하기 때문에 필수라고 생각하는 값으로 목록이나 딕셔너리를 반환하는 것을 지원하지 않았기 때문 입니다.

그래서 나는 timeit모듈을 통해 10,000 번의 반복으로 다른 기능을 펌핑하고 다음과 같은 결과를 얻었습니다.

0.11 usec/pass on gen_dict_extract(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6.03 usec/pass on find_all_items(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0.15 usec/pass on findkeys(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1.79 usec/pass on get_recursively(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0.14 usec/pass on find(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0.36 usec/pass on dict_extract(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -

모든 함수에는 검색 할 동일한 바늘 ( 'logging')과 동일한 사전 객체가 있으며 다음과 같이 구성됩니다.

o = { 'temparature': '50', 
      'logging': {
        'handlers': {
          'console': {
            'formatter': 'simple', 
            'class': 'logging.StreamHandler', 
            'stream': 'ext://sys.stdout', 
            'level': 'DEBUG'
          }
        },
        'loggers': {
          'simpleExample': {
            'handlers': ['console'], 
            'propagate': 'no', 
            'level': 'INFO'
          },
         'root': {
           'handlers': ['console'], 
           'level': 'DEBUG'
         }
       }, 
       'version': '1', 
       'formatters': {
         'simple': {
           'datefmt': "'%Y-%m-%d %H:%M:%S'", 
           'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
         }
       }
     }, 
     'treatment': {'second': 5, 'last': 4, 'first': 4},   
     'treatment_plan': [[4, 5, 4], [4, 5, 4], [5, 5, 5]]
}

모든 기능이 동일한 결과를 제공했지만 시간 차이는 극적입니다! 이 함수 gen_dict_extract(k,o)는 여기 함수에서 조정 된 내 함수입니다. 실제로 findAlfe 함수 와 거의 비슷 하지만, 재귀 중에 문자열이 전달되는 경우 주어진 객체에 iteritems 함수가 있는지 확인하고 있습니다.

def gen_dict_extract(key, var):
    if hasattr(var,'iteritems'):
        for k, v in var.iteritems():
            if k == key:
                yield v
            if isinstance(v, dict):
                for result in gen_dict_extract(key, v):
                    yield result
            elif isinstance(v, list):
                for d in v:
                    for result in gen_dict_extract(key, d):
                        yield result

따라서이 변형은 여기에서 가장 빠르고 안전한 기능입니다. 그리고 find_all_items엄청나게 느리고 두 번째로 느리지 get_recursivley만 나머지 dict_extract는 서로 가깝습니다. 기능 funkeyHole유일한 작업은 문자열을 찾고 있다면.

흥미로운 학습 측면 :)


d = { "id" : "abcde",
    "key1" : "blah",
    "key2" : "blah blah",
    "nestedlist" : [ 
    { "id" : "qwerty",
        "nestednestedlist" : [ 
        { "id" : "xyz", "keyA" : "blah blah blah" },
        { "id" : "fghi", "keyZ" : "blah blah blah" }],
        "anothernestednestedlist" : [ 
        { "id" : "asdf", "keyQ" : "blah blah" },
        { "id" : "yuiop", "keyW" : "blah" }] } ] } 


def fun(d):
    if 'id' in d:
        yield d['id']
    for k in d:
        if isinstance(d[k], list):
            for i in d[k]:
                for j in fun(i):
                    yield j

>>> list(fun(d))
['abcde', 'qwerty', 'xyz', 'fghi', 'asdf', 'yuiop']

def find(key, value):
  for k, v in value.iteritems():
    if k == key:
      yield v
    elif isinstance(v, dict):
      for result in find(key, v):
        yield result
    elif isinstance(v, list):
      for d in v:
        for result in find(key, d):
          yield result

편집 : @Anthon은 이것이 직접 중첩 된 목록에서 작동하지 않는다는 것을 알았습니다. 입력에 이것을 가지고 있다면 이것을 사용할 수 있습니다.

def find(key, value):
  for k, v in (value.iteritems() if isinstance(value, dict) else
               enumerate(value) if isinstance(value, list) else []):
    if k == key:
      yield v
    elif isinstance(v, (dict, list)):
      for result in find(key, v):
        yield result

하지만 원래 버전이 이해하기 더 쉽다고 생각하므로 그대로 두겠습니다.


d = { "id" : "abcde",
    "key1" : "blah",
    "key2" : "blah blah",
    "nestedlist" : [
    { "id" : "qwerty",
        "nestednestedlist" : [
        { "id" : "xyz", "keyA" : "blah blah blah" },
        { "id" : "fghi", "keyZ" : "blah blah blah" }],
        "anothernestednestedlist" : [
        { "id" : "asdf", "keyQ" : "blah blah" },
        { "id" : "yuiop", "keyW" : "blah" }] } ] }


def findkeys(node, kv):
    if isinstance(node, list):
        for i in node:
            for x in findkeys(i, kv):
               yield x
    elif isinstance(node, dict):
        if kv in node:
            yield node[kv]
        for j in node.values():
            for x in findkeys(j, kv):
                yield x

print(list(findkeys(d, 'id')))

나는 yield from최상위 목록을 사용 하고 수락 하는 @ hexerei-software의 훌륭한 대답을 반복하고 싶었습니다 .

def gen_dict_extract(var, key):
    if isinstance(var, dict):
        for k, v in var.items():
            if k == key:
                yield v
            if isinstance(v, (dict, list)):
                yield from gen_dict_extract(v, key)
    elif isinstance(var, list):
        for d in var:
            yield from gen_dict_extract(d, key)

내가 한 방법은 다음과 같습니다.

이 함수는 중첩 된 사전과 목록을 포함하는 사전을 재귀 적으로 검색합니다. 필드가 발견 될 때마다 값을 포함하는 fields_found라는 목록을 작성합니다. '필드'는 사전과 중첩 된 목록 및 사전에서 찾고있는 키입니다.

def get_recursively (search_dict, field) :
    "" "중첩 된 목록과 사전이있는 사전을 취합니다.
    필드의 키에 대한 모든 사전을 검색합니다.
    제공됩니다.
    "" "
    fields_found = []

    키의 경우 search_dict.iteritems ()의 값 :

        키 == 필드 인 경우 :
            fields_found.append (값)

        elif isinstance(value, dict):
            results = get_recursively(value, field)
            for result in results:
                fields_found.append(result)

        elif isinstance(value, list):
            for item in value:
                if isinstance(item, dict):
                    more_results = get_recursively(item, field)
                    for another_result in more_results:
                        fields_found.append(another_result)

    return fields_found

Another variation, which includes the nested path to the found results (note: this version doesn't consider lists):

def find_all_items(obj, key, keys=None):
    """
    Example of use:
    d = {'a': 1, 'b': 2, 'c': {'a': 3, 'd': 4, 'e': {'a': 9, 'b': 3}, 'j': {'c': 4}}}
    for k, v in find_all_items(d, 'a'):
        print "* {} = {} *".format('->'.join(k), v)    
    """
    ret = []
    if not keys:
        keys = []
    if key in obj:
        out_keys = keys + [key]
        ret.append((out_keys, obj[key]))
    for k, v in obj.items():
        if isinstance(v, dict):
            found_items = find_all_items(v, key, keys=(keys+[k]))
            ret += found_items
    return ret

Here is my stab at it:

def keyHole(k2b,o):
  # print "Checking for %s in "%k2b,o
  if isinstance(o, dict):
    for k, v in o.iteritems():
      if k == k2b and not hasattr(v, '__iter__'): yield v
      else:
        for r in  keyHole(k2b,v): yield r
  elif hasattr(o, '__iter__'):
    for r in [ keyHole(k2b,i) for i in o ]:
      for r2 in r: yield r2
  return

Ex.:

>>> findMe = {'Me':{'a':2,'Me':'bop'},'z':{'Me':4}}
>>> keyHole('Me',findMe)
<generator object keyHole at 0x105eccb90>
>>> [ x for x in keyHole('Me',findMe) ]
['bop', 4]

참고URL : https://stackoverflow.com/questions/9807634/find-all-occurrences-of-a-key-in-nested-python-dictionaries-and-lists

반응형