patternpythonMinor
Resolving multiple "paths" in nested attributes
Viewed 0 times
pathsnestedattributesmultipleresolving
Problem
I need to resolve "multiple paths" specified like so:
A list like this is expected:
Code:
Several tests I wrote for this function pass, but I'm not entirely happy with this solution, it's somewhat convoluted. Better solutions? Simplifications?
p1 = 'item.element.[compact|fullsize].documents.[note|invoice]'A list like this is expected:
['item.element.compact.documents.note', 'item.element.fullsize.documents.note',
'item.element.compact.documents.invoice', 'item.element.fullsize.documents.invoice']Code:
def resolve_paths(path):
parts = path.split('.')
depth = len(parts)
new_paths = []
for level, part in enumerate(parts):
mult_branches = re.findall(r'\[(\w+)(?:\|(\w+))*\]', part)
if mult_branches:
mult_branches = flatten_iterable(mult_branches)
for branch in mult_branches:
interm_path = '.'.join(parts[:level] + [branch] + parts[level+1:])
new_paths.extend(resolve_paths(interm_path))
return new_paths
elif level == depth - 1:
new_paths.append(path)
return new_pathsSeveral tests I wrote for this function pass, but I'm not entirely happy with this solution, it's somewhat convoluted. Better solutions? Simplifications?
Solution
What do you think this will return for
It will give:
Because, when a group matches multiple times, the last match overwrites previous matches, as per the docs.
If you want to support multiple patterns, I'm afraid you will have to do in two steps: matching and then splitting.
The part I don't like about this is that you split path to parts,
then when you need to recurse,
you re-join the parts again,
and in the recursion step it will be split again, and so on.
That's a lot of splitting and joining and iterations.
The recursive logic can be also tricky to understand.
Here's an alternative, without using recursion and unnecessary splitting, joining, iteration:
part = '[compact|fullsize|x|y]'re.findall(r'\[(\w+)(?:\|(\w+))*\]', part)It will give:
[('compact', 'y')]Because, when a group matches multiple times, the last match overwrites previous matches, as per the docs.
If you want to support multiple patterns, I'm afraid you will have to do in two steps: matching and then splitting.
The part I don't like about this is that you split path to parts,
then when you need to recurse,
you re-join the parts again,
and in the recursion step it will be split again, and so on.
That's a lot of splitting and joining and iterations.
The recursive logic can be also tricky to understand.
Here's an alternative, without using recursion and unnecessary splitting, joining, iteration:
from collections import deque
def resolve_paths(path):
parts = deque(path.split('.'))
paths = [[]]
while parts:
part = parts.popleft()
branches = re.findall(r'\[(\w+)(?:\|(\w+))*\]', part)
if branches:
orig_paths = paths[:]
paths = []
for branch in branches[0]:
for path in orig_paths:
paths.append(path + [branch])
else:
for path in paths:
path.append(part)
return ['.'.join(path) for path in paths]Code Snippets
re.findall(r'\[(\w+)(?:\|(\w+))*\]', part)[('compact', 'y')]from collections import deque
def resolve_paths(path):
parts = deque(path.split('.'))
paths = [[]]
while parts:
part = parts.popleft()
branches = re.findall(r'\[(\w+)(?:\|(\w+))*\]', part)
if branches:
orig_paths = paths[:]
paths = []
for branch in branches[0]:
for path in orig_paths:
paths.append(path + [branch])
else:
for path in paths:
path.append(part)
return ['.'.join(path) for path in paths]Context
StackExchange Code Review Q#98697, answer score: 5
Revisions (0)
No revisions yet.