2013-04-26 4 views
1

Я хочу сравнить значения из двух переменных (словарь и список). Словарь имеет вложенную конструкцию, поэтому мне нужно перебрать все элементы. Я обнаружил простое решение, но я уверен, что могу сделать это лучше (используя python). Вкратце я хочу найти предметы от user_from_database, которые не существуют в переменной user_from_client.Сравнение значения из вложенных словарей и списка

Мое решение:

#variable containing users from client side 
users_from_client = { 
    "0": { 
    "COL1": "whatever", 
    "COL2": "val1", 
    "COL3": "whatever", 
    }, 
    "1": { 
    "COL1": "whatever", 
    "COL2": "val2", 
    "COL3": "whatever", 
    }, 
    "3": { 
    "COL1": "whatever", 
    "COL2": "val3", 
    "COL3": "whatever", 
    }  
} 

#variable containing users from the database 
users_from_database = [ 
    ["val1"], 
    ["val2"], 
    ["val5"], 
    ["val7"] 
] 

#This function is used to find element from the nested dictionaries(d) 
def _check(element, d, pattern = 'COL2'): 
    exist = False 
    for k, user in d.iteritems(): 
    for key, item in user.iteritems(): 
     if key == pattern and item == element: 
     exist = True 
    return exist 

#Finding which users should be removed from the database 
to_remove = [] 
for user in users_from_db: 
    if not _check(user[0], users_from_agent): 
    if user[0] not in to_remove: 
     to_remove.append(user[0]) 

#to_remove list contains: [val5, val7"] 

Что такое лучший способ дать тот же результат, используя питона подход? Возможно, мне не нужно добавлять, что я новичок с python (я предполагаю, что вы можете видеть, что смотрите код выше).

+1

Почему вы включаете каждый «val» в собственный список в 'users_from_database'? –

ответ

1

Просто используйте error-safe dictionary lookup:

def _check(element, d, pattern = 'COL2'): 
    for user in d.itervalues(): 
     if user.get(pattern) == element: 
      return True 
    return False 

Или как один лайнер:

def _check(element, d, pattern = 'COL2'): 
    return any(user.get(pattern) == element for user in d.itervalues()) 

Или пытается сделать всю работу в качестве однострочника:

#Finding which users should be removed from the database 
to_remove = set(
    name 
    for name in users_from_database.itervalues() 
    if not any(user.get('COL2') == name for (user,) in users_from_client) 
) 

assert to_remove == {"val5", "val7"} 

set s can сделать его еще более кратким (и эффективно):

to_remove = set(
    user for (user,) in users_from_database 
) - set(
    user.get('COL2') for user in users_from_client 
) 

Ваши структуры данных немного странно. Рассмотрите возможность использования:

users_from_client = [ 
    { 
    "COL1": "whatever", 
    "COL2": "val1", 
    "COL3": "whatever", 
    }, { 
    "COL1": "whatever", 
    "COL2": "val2", 
    "COL3": "whatever", 
    }, { 
    "COL1": "whatever", 
    "COL2": "val3", 
    "COL3": "whatever", 
    } 
] 

#variable containing users from the database 
users_from_database = set(
    "val1", 
    "val2", 
    "val5", 
    "val7" 
) 

Что уменьшает ваш код:

to_remove = users_from_database - set(
    user.get('COL2') for user in users_from_client 
) 
+0

Обратите внимание, что для этого требуется убедиться, что 'None' не является допустимым значением словаря. – Antimony

+0

Если честно, может возникнуть больше смысла просто выбросить «KeyError». В качестве взлома вы можете использовать 'user.get ('COL2', float ('nan')) ' – Eric

+0

Я знаю, что это выглядит не очень хорошо, но, к сожалению, я получаю эту структуру от одной функции. Конечно, я могу добавить еще одну итерацию для удаления ненужного списка, но я не вижу смысла в этом подходе (еще одна итерация ...) – user1119698

0

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

Прежде всего, вы не используете k, чтобы вы могли также перебирать только значения. Во-вторых, вам не нужно отслеживать exists, вы можете сразу же вернуться, когда найдете совпадение. Наконец, если вы проверяете пару с ключом, вы можете просто проверить, содержит ли кортеж в элементах.

def _check(element, d, pattern = 'COL2'): 
    for user in d.itervalues(): 
    if (pattern, element) in user.items(): 
     return True 
    return False 
+0

Thx, ваш код выглядит намного лучше, чем мой. Сравнение кортежей велико! – user1119698

0

Вы можете создать перевернутую Dict для быстрого поиска и поместить его в кэш-памяти, например ..

>>> from collections import defaultdict 
>>> 
>>> users_inverted = defaultdict(list) 
>>> for pk, user in users_from_client.iteritems(): 
... for key in user.iteritems(): 
... users_inverted[key].append(int(pk)) 
... 
>>> users_inverted 
defaultdict(<type 'list'>, {('COL3', 'whatever'): [1, 0, 3], ('COL2', 'val1'): [0], ('COL1', 'whatever'): [1, 0, 3], ('COL2', 'val2'): [1], ('COL2', 'val3'): [3]}) 

, а затем LookUp для пользователей будет очень быстро:

>>> def _check(element, pattern = 'COL2'): 
... return bool(users_inverted[(pattern, element)]) 
>>> 
>>> _check('whatever', 'COL3') 
True 
>>> _check('whatever', 'COL333') 
False 

и в качестве плюса помимо скорости вы получаете список пользователей для каждой пары атрибутов

Смежные вопросы