У меня есть простая структура данных, примерно следующая, с произвольной глубиной:
# Level 0
{ 'foo': 'bar', 'more':
[ # Level 1
{ 'foo': 'can', 'more': [ # Level 2
{ 'foo': 'haz', 'more': [] }
]
},
{ 'foo': 'baz', 'more': [ # Level 2
{ 'foo': None, 'more': [] }
]
}
]
}
Работа над функцией для вставки на определенном уровне. Если значение foo
равно None
, введите value
, в противном случае вставьте новый «брат», т. Е. Новый dict
на том же уровне. Помимо 0-го уровня, все находится в пределах list
, поэтому должен быть append
способный.
Попытка:
def traverse_to_level(obj, level):
if obj['_level'] == level:
return obj
for _obj in obj['block']:
found = traverse_to_level(_obj, level)
if found is not None:
return found
return None
def set_obj(top_obj, value, level):
obj = traverse_to_level(top_obj, level)
if obj is None:
directive = traverse_to_level(top_obj, level - 1)
assert obj is not None
if obj['foo'] is None:
obj['foo'] = value
elif obj['_level'] < level:
obj['more'].append({'foo': value, 'more': [], '_level': level})
else:
obj = traverse_to_level(top_obj, level - 1)
obj['more'].append({'foo': value, 'more': [], '_level': level})
return obj
Что такое правильный путь для обхода структуры, подобной этой, и обновления ее на месте с помощью level
?
РЕДАКТИРОВАТЬ: еще один пример, показывающий больше, чем просто 'foo' на каждом объекте. Для упрощения: если ввод начинается с #
, то его следует добавить к alpha
, в противном случае beta
. Если любой из них уже заполнен, вставьте соседний (т. Е. Новый брат {'alpha': None, 'beta': None, 'more': []}
).
def make_ab(alpha=None, beta=None, more=None):
return {
'alpha': alpha,
'beta': beta,
'more': more or []
}
def parse(arguments):
level, last_level_change, top_d = 0, -1, make_ab()
for idx, arg in enumerate(arguments):
if arg == '#{':
level += 1
elif arg in frozenset(('#d', '##dog')):
level += 1
last_level_change = idx
elif arg == '#}':
level -= 1
elif idx == last_level_change - 1:
set_obj(top_d, arg, level, 'alpha')
elif idx == last_level_change - 2:
set_obj(top_d, arg, level, 'beta')
else:
set_obj(top_d, arg, level,
'beta' if arg.startswith('##')
else 'alpha')
return top_d
Использование, намекает на ожидаемую иерархию через отступ:
actual = parse(
('#d',
'definite',
'##foo_alpha_5', 'nice_beta',
'##name_2', 'amazing_beta',
'##dog',
'##fancy_name', 'fancy_BETA_f',
'##one_AL_PHA_of', 'I_C_U_B_ETA',
'#}',
'#}')
)
И это ожидаемый результат:
{
'alpha': 'definite',
'beta': None,
'more': [
{
'alpha': '##foo_alpha_5',
'beta': 'nice_beta',
'more': []
},
{
'alpha': '##name_2',
'beta': 'amazing_beta',
'more': [
{
'alpha': '##fancy_name',
'beta': 'fancy_BETA_f',
'more': []
},
{
'alpha': '##one_AL_PHA_of',
'beta': 'I_C_U_B_ETA',
'more': []
}
]
}
]
}