Подтвердить что ты не робот

Получить количество всех ключей в словаре словарей в Python

У меня есть словарь словарей в Python 2.7.

Мне нужно быстро подсчитать количество всех клавиш, включая клавиши в каждом из словарей.

Итак, в этом примере мне понадобится число всех ключей: 6:

dict_test = {'key2': {'key_in3': 'value', 'key_in4': 'value'}, 'key1': {'key_in2': 'value', 'key_in1': 'value'}}

Я знаю, что я могу перебирать каждый ключ с помощью циклов, но я ищу более быстрый способ сделать это, так как у меня будут тысячи/миллионы ключей, и это просто неэффективно:

count_the_keys = 0

for key in dict_test.keys():
    for key_inner in dict_test[key].keys():
       count_the_keys += 1

# something like this would be more effective
# of course .keys().keys() doesn't work
print len(dict_test.keys()) * len(dict_test.keys().keys())
4b9b3361

Ответ 1

Сохранение простоты

Если мы знаем, что все значения являются словарями и не хотят проверять, что любые их значения также являются словарями, то это так же просто, как:

len(dict_test) + sum(len(v) for v in dict_test.itervalues())

Немного переделайте, чтобы убедиться, что значения являются словарями перед их подсчету:

len(dict_test) + sum(len(v) for v in dict_test.itervalues() if isinstance(v, dict))

И, наконец, если вы хотите сделать произвольную глубину, что-то вроде следующего:

def sum_keys(d):
    return (0 if not isinstance(d, dict) 
            else len(d) + sum(sum_keys(v) for v in d.itervalues())

print sum_keys({'key2': {'key_in3': 'value', 'key_in4': 'value'}, 
                'key1': {'key_in2': 'value', 
                         'key_in1': dict(a=2)}})
# => 7

В этом последнем случае мы определяем функцию, которая будет называться рекурсивно. Учитывая значение d, мы возвращаем либо:

  • 0, если это значение не является словарем; или
  • количество ключей в словаре, а также общее количество ключей у всех наших детей.

Быстрее

Вышеприведенный и понятный подход. Мы можем получить немного быстрее, используя генератор:

def _counter(d):
    # how many keys do we have?
    yield len(d)

    # stream the key counts of our children
    for v in d.itervalues():
        if isinstance(v, dict):
            for x in _counter(v):
                yield x

def count_faster(d):
    return sum(_counter(d))

Это дает нам немного большую производительность:

In [1]: %timeit sum_keys(dict_test)
100000 loops, best of 3: 4.12 µs per loop

In [2]: %timeit count_faster(dict_test)
100000 loops, best of 3: 3.29 µs per loop

Ответ 2

Как насчет

n = sum([len(v)+1 for k, v in dict_test.items()])

То, что вы делаете, это итерация по всем клавишам k и значениям v. Значения v являются вашими поддисками. Вы получаете длину этих словарей и добавляете их для включения ключа, используемого для индексации подзадачи.

Затем вы суммируете по списку, чтобы получить полное количество ключей.

EDIT:

Чтобы уточнить, этот фрагмент работает только для словарей словарей по запросу. Не словари словарей словарей...
Поэтому не используйте его для вложенного примера:)

Ответ 3

В качестве более общего способа вы можете использовать функцию рекурсии и выражения генератора:

>>> def count_keys(dict_test):
...     return sum(1+count_keys(v) if isinstance(v,dict) else 1 for _,v in dict_test.iteritems())
... 

Пример:

>>> dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}
>>> 
>>> count(dict_test)
8

Примечание. В python 3.X используйте dict.items() метод вместо iteritems().

Тест с принятым ответом, который показывает, что эта функция быстрее принятого ответа:

from timeit import timeit

s1 = """
def sum_keys(d):
    return 0 if not isinstance(d, dict) else len(d) + sum(sum_keys(v) for v in d.itervalues())

sum_keys(dict_test)
"""

s2 = """
def count_keys(dict_test):
    return sum(1+count_keys(v) if isinstance(v,dict) else 1 for _,v in dict_test.iteritems())

count_keys(dict_test)
   """

print '1st: ', timeit(stmt=s1,
                      number=1000000,
                      setup="dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}")
print '2nd : ', timeit(stmt=s2,
                       number=1000000,
                       setup="dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}")

результат:

1st:  4.65556812286
2nd :  4.09120802879

Ответ 4

Использование функции-генератора и yield from синтаксис new в Python 3.x. Это будет работать для произвольного вложенного словаря

>>> from collections import Mapping
>>> def count_keys(mydict):
...     for key, value in mydict.items():
...         if isinstance(value, Mapping):
...             yield from count_keys(value)
...     yield len(mydict)
... 
>>> dict_test = {'key2': {'key_in3': 'value', 'key_in4': 'value'}, 'key1': {'key_in2': 'value', 'key_in1': 'value'}}
>>> sum(count_keys(dict_test))
6

В Python 2.x вам нужно сделать это:

>>> def count_keys(mydict):
...     for key, value in mydict.items():
...         if isinstance(value, Mapping):
...             for item in count_keys(value):
...                 yield 1
...         yield 1
... 
>>> sum(count_keys(dict_test))
6

Ответ 5

Что-то вроде:

print len(dict_test) + sum(len(v) for v in dict_test.values())

Ответ 6

Вот рекурсивная функция, чтобы найти общее количество ключей вложенных словарей...

s=0
def recurse(v):
   if type(v)==type({}):
     for k in v.keys():
      global s
      s+=1
      recurse(v[k])

Ответ 7

Вы можете попробовать использовать pandas DataFrame для этого:

>>> import pandas as pd
>>> data = {'1': {'2': 'a', '3': 'b'}, '4': {'5': 'c', '6': 'd'}, '7': {'5': 'x'}}
>>> df = pd.DataFrame(data)
>>> print (df.count().sum() + len(df.columns))  # 8

Линия pd.DataFrame(data) преобразует ваш словарь в матрицу N x M, где N - число "родительских" ключей, а M - количество уникальных дочерних ключей:

     1    4    7
2    a  NaN  NaN
3    b  NaN  NaN
5  NaN    c    x
6  NaN    d  NaN

Для каждой [строки, столбца] у вас есть значение или NaN. Вам просто нужно подсчитать значения не NaN, что даст вам количество дочерних ключей и добавит len(df.columns), что означает количество столбцов (т.е. Родительских ключей).

Ответ 8

рекурсивная функция:

def count_keys(some_dict):
    count = 0
    for key in some_dict:
        if isinstance(some_dict[key], dict):
            count += count_keys(some_dict[key])
        count += 1
    return count

Ответ 9

len (dict) вернет количество ключей в словаре, поэтому, предполагая, что вы знаете, как он вложен, и что все значения являются словарями:

counter = len(outer_dict)
for v in outer_dict.values :
    counter += len(v)

Вы можете обернуть это в понимании списка:

counter = len(outer_dict)
counter += sum([len(inner_dict) for inner_dict in outer_dict.values])

который, вероятно, самый пифонический. Вы можете расширить его как:

counter = len(outer_dict)
counter += sum([len(inner_dict) if isinstance(inner_dict, dict) else 0 for inner_dict in outer_dict.values])

но я склонен думать, что это довольно непроницаемо.

Ответ 10

Попробуйте это,

l = len(dict_test)
for k in dict_test:
    l += len(dict_test[k])