Найти - Пользователи
Полная версия: Сложная рекурсия по списку словарей списков
Начало » Python для новичков » Сложная рекурсия по списку словарей списков
1
rick1177
Товарищи, добрый день.
Я получаю сложные структуры, которые могу являть собой разные сочетания вложенных словарей и списков друг в друга. Например, вот такие наборы:
 a={'tr': {'td': [{'attributes': {'width': '7%'}}, {'text': 'eLIBRARY ID:', 'a': {'text': '58061', 'attributes': {'href': 'title_about_new.asp?id=58061'}}, 'attributes': {'width': '43%', 'align': 'left'}}, {'text': 'Язык описания:', 'font': {'text': 'русский', 'attributes': {'color': '#000000'}}, 'attributes': {'width': '48%', 'align': 'right'}}, {'attributes': {'width': '2%'}}], 'attributes': {'valign': 'middle'}}, 'attributes': {'width': '550', 'cellspacing': '0', 'cellpadding': '3', 'border': '0'}}
a= [{'1':1, "2":[{'3':3},{'4':[{'11':11, '12':12, 'attributes':'pf'}, {'attributes':'pf'}]}, {'attributes':'pf'}], '5':5, 'attributes':'pf'}]
a= [{'1':1, "2":[{'3':3},{'4':4}, {'attributes':'pf'}], '5':5, 'attributes':'pf'}]
a={'1':1, "2":[{'3':3},{'4':4}, {'attributes':'pf'}], '5':5, 'attributes':'pf'}
a=[{'3':3},{'4':4}, {'attributes':'pf'}]
a={'3':3, '4':4, 'attributes':'pf'}
a={'3':{'4':4, 'attributes':'pf'},'5':3}
a={'3':{'4':4, 'attributes':'pf'},'5':3, '6':{'attributes':'pf','7':7, '8':{'9':9, 'attributes':'pf', '10':10}}, 'attributes':'pf'}

Поскольку я не могу знать заранее количество уровней вложенности, то, единственный способ, который мне пришёл в голову - рекурсия.
Мне удалось написать функцию, которая делает почти то, что мне нужно:
 def del_element(dict_or_list):
    if isinstance(dict_or_list,list):
        res=[]
        for i in dict_or_list:
            res.extend(del_element(i))
        yield res
    if isinstance(dict_or_list,dict):
        del_key = ('attributes',)
        for key in del_key:
            if key in dict_or_list:
                del dict_or_list[key]
        for k, v in dict_or_list.items():
            if isinstance(v, dict) and (len(v)!=0):
                yield from del_element(v)
            if isinstance(v, list):
                res = []
                for i in v:
                    res.extend(del_element(i))
                for i in range(len(res)):
                    if len(res[i]) == 0:
                        del res[i]
                yield res
        if (len(dict_or_list)!=0):
            yield dict_or_list

В итоге, я получаю объект-генератор, с которым дальше работаю так:
 a = del_element(a)
for b in a: b=b
print(b)

Но вот проблема, не знаю как с этим справиться. После работы функции на примере выражения:
 a= [{'1':1, "2":[{'3':3},{'4':[{'11':11, '12':12, 'attributes':'pf'}, {'attributes':'pf'}]}, {'attributes':'pf'}], '5':5, 'attributes':'pf'}]
я получаю такой вывод:
[[{'3': 3}, , {'4': [{'11': 11, ‘12’: 12}, {}]}], {'1': 1, ‘2’: [{'3': 3}, {'4': [{'11': 11, ‘12’: 12}, {}]}, {}], ‘5’: 5}]

В выводе видно, что есть пустые словари: {}

Подскажите, плиз, как избавиться от них?
xam1816
  
a = [{'1': 1,
      "2": [{'3': 3}, {'4': [{'11': 11, '12': 12, 'attributes': 'pf'}, {'attributes': 'pf'}]}, {'attributes': 'pf'}],
      '5': 5, 'attributes': 'pf'}]
def filter_data(data):
    if isinstance(data, list):
        return [val for i in data if (val := filter_data(i))]
    elif isinstance(data, dict):
        return {k: val for k, v in data.items() if k != 'attributes' and (val := filter_data(v))}
    else:
        return data
print(filter_data(a))
вывод
[{'1': 1, '2': [{'3': 3}, {'4': [{'11': 11, '12': 12}]}], '5': 5}]
rick1177
Добрый день.
Можно ли попросить вас пояснить эти строки:
xam1816
 return [val for i in data if (val := filter_data(i))]
xam1816
 return {k: val for k, v in data.items() if k != 'attributes' and (val := filter_data(v))}
В частности, первый раз вижу конструкцию:
xam1816
 val := filter_data(i)

ntram
VANISHED
xam1816
rick1177
В частности, первый раз вижу конструкцию:
val := filter_data(i)
без нее можно написать вот так
  
[filter_data(i) for i in data if filter_data(i)]
но так приходится два раза задействовать функцию, поэтому ввели оператор := для таких случаев

а вообще гуглите вложенные списки, генераторы, итераторы, и тд и тп.
rick1177
xam1816
без нее можно написать вот так:
 [filter_data(i) for i in data if filter_data(i)]

А как бы тогда выглядела эта строка? (что-то туплю)
xam1816
 return {k: val for k, v in data.items() if k != 'attributes' and (val := filter_data(v))}

rick1177
xam1816
без нее можно написать вот так
Попытался сделать самостоятельно, но что-то не получается:
 def filter_data_2(data):
    if isinstance(data, list):
        return [filter_data(i) for i in data if filter_data(i)]
    elif isinstance(data, dict):
        return {k: filter_data(v) for k, v in data.items() if k != 'attributes' and (filter_data(v))}
    else:
        return data

Результат сильно плохой:
 tr
{'1': 1, '2': [{'3': 3}, {'4': [{'11': 11, '12': 12}]}], '5': 5}
{'1': 1, '2': [{'3': 3}, {'4': 4}], '5': 5}
5
{'4': 4}
4
5
6

Я видимо совсем не понял этой конструкции:
 return {k: val for k, v in data.items() if k != 'attributes' and (val := filter_data(v))}

Что такое двоеточие после k, что здесь происходит?

This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB