Reputation: 695
Need to turn x:
X = [['A', 'B', 'C'], ['A', 'B', 'D']]
Into Y:
Y = {'A': {'B': {'C','D'}}}
More specifically, I need to create a tree of folders and files from a list of absolute paths, which looks like this:
paths = ['xyz/123/file.txt', 'abc/456/otherfile.txt']
where, each path is split("/")
, as per ['A', 'B', 'C']
in the pseudo example.
As this represents files and folders, obviously, on the same level (index of the array) same name strings can't repeat.
Upvotes: 16
Views: 19631
Reputation: 1479
First split keys from values
x = [['A', 'B', 'C'], ['A', 'B', 'D']]
keys = [tuple(asd[:-1]) for asd in x]
values = [asd[-1] for asd in x]
Now use them to populate a NestedDict
from ndicts.ndicts import NestedDict
nd = NestedDict()
for key, value in zip(keys, values):
nd[key] = value
>>> nd
NestedDict({'A': {'B': 'D'}})
>>> nd.to_dict()
{'A': {'B': 'D'}}
To install ndicts pip install ndicts
Upvotes: 0
Reputation: 31
I got asked about this question on twitter and came up with this slick solution using functional programming which I figure I might as well share here.
from functools import reduce
X = [['A', 'B', 'C'], ['A', 'B', 'D']]
Y = [reduce(lambda x, y: {y:x}, Y[::-1]) for Y in X]
which returns:
[{'A': {'B': 'C'}}, {'A': {'B': 'D'}}]
as desired.
For the simpler problem where you have one list that you want to represent as a dict with nested keys, this will suffice:
from functools import reduce
X = ['A', 'B', 'C']
reduce(lambda x, y: {y:x}, X[::-1])
which returns:
{'A': {'B': 'C'}}
Upvotes: 1
Reputation: 45562
X = [['A', 'B', 'C'], ['A', 'B', 'D'],['W','X'],['W','Y','Z']]
d = {}
for path in X:
current_level = d
for part in path:
if part not in current_level:
current_level[part] = {}
current_level = current_level[part]
This leaves us with d containing {'A': {'B': {'C': {}, 'D': {}}}, 'W': {'Y': {'Z': {}}, 'X': {}}}
. Any item containing an empty dictionary is either a file or an empty directory.
Upvotes: 33
Reputation: 16625
Assuming that {'C', 'D'}
means set(['C', 'D'])
and your Python version supports dict comprehension
and set comprehension
, here's an ugly but working solution:
>>> tr = [[1, 2, 3], [1, 2, 4], [5, 6, 7]]
>>> {a[0]: {b[1]: {c[2] for c in [y for y in tr if y[1] == b[1]]} for b in [x for x in tr if x[0] == a[0]]} for a in tr}
{1: {2: set([3, 4])}, 5: {6: set([7])}}
As for your example:
>>> X = [['A', 'B', 'C'], ['A', 'B', 'D']]
>>> {a[0]: {b[1]: {c[2] for c in [y for y in X if y[1] == b[1]]} for b in [x for x in X if x[0] == a[0]]} for a in X}
{'A': {'B': set(['C', 'D'])}}
But please don't use it in a real-world application :)
UPDATE: here's one that works with arbitrary depths:
>>> def todict(lst, d=0):
... print lst, d
... if d > len(lst):
... return {}
... return {a[d]: todict([x for x in X if x[d] == a[d]], d+1) for a in lst}
...
>>> todict(X)
{'A': {'B': {'C': {}, 'D': {}}}}
Upvotes: 6
Reputation: 40243
This should be pretty close to what you need:
def path_to_dict(path):
parts = path.split('/')
def pack(parts):
if len(parts) == 1:
return parts
elif len(parts):
return {parts[0]: pack(parts[1:])}
return parts
return pack(parts)
if __name__ == '__main__':
paths = ['xyz/123/file.txt', 'abc/456/otherfile.txt']
for path in paths:
print '%s -> %s' % (path, path_to_dict(path))
Results in:
xyz/123/file.txt -> {'xyz': {'123': ['file.txt']}}
abc/456/otherfile.txt -> {'abc': {'456': ['otherfile.txt']}}
Upvotes: 1
Reputation: 19377
There is a logical inconsistency in your problem statement. If you really want
['xyz/123/file.txt', 'abc/456/otherfile.txt']
to be changed to
{'xyz': {'123': 'file.txt}, 'abc': {'456': 'otherfile.txt'}}
Then you have to answer how a path 'abc.txt' with no leading folder would be inserted into this data structure. Would the top-level dictionary key be the empty string ''
?
Upvotes: 1