1

I have a list of paths and contents similar to that:

paths = [
    ("/test/file1.txt", "content1"),
    ("/test/file2.txt", "content2"),
    ("/file3.txt", "content3"),
    ("/test1/test2/test3/file5.txt", "content5"),
    ("/test2/file4.txt", "content4")
]

I would transform this path list to:

structure = {
    "file3.txt": "content3"
    "test": {
        "file1.txt": "content1",
        "file2.txt": "content2"
    },
    "test2": {
        "file4.txt": "content4"
    }
}

Is there any simple solution to that problem ?

John Doe
  • 235
  • 1
  • 9

3 Answers3

0

Try using recursivity:

paths = [
    ("/test/file1.txt", "content1"),
    ("/test/file2.txt", "content2"),
    ("/file3.txt", "content3"),
    ("/test2/file4.txt", "content4"),
    ('/test1/test2/test3/file.txt', 'content'),
    ('/test10/test20/test30/test40/file.txt', 'content100')
]

def create_structure(elems,count,mylen,p_1,var):
    if mylen<=2:
        var[elems[count]] = p_1
        return
    create_structure(elems,count+1,mylen-1,p_1,var.setdefault(elems[count],{}))

structure = {}
for p in paths:
    elems = p[0].split('/')
    create_structure(elems,1,len(elems),p[1],structure)

print structure
  • Sorry, but my example was not good. My list contains paths whose depth is greater, like `('/test1/test2/test3/file.txt', 'content')`. – John Doe Feb 13 '16 at 22:41
0

I think .setdefault() be ok:

paths = [
    ("/test/file1.txt", "content1"),
    ("/test/file2.txt", "content2"),
    ("/file3.txt", "content3"),
    ("/test2/file4.txt", "content4")
]

dirs = {}

for p in paths:
    current = dirs
    ps =  p[0].split('/')
    for d in ps[:-1]:
        if d:
            current = current.setdefault(d, {})
    current[ps[-1]] = p[1]

print(dirs)
steel.ne
  • 665
  • 5
  • 6
0

Since the file path can be of an arbitrary depth, we need something scalable.

Here is a recursive approach - splitting the path recursively until we get to the root /:

import os

paths = [
    ("/test/file1.txt", "content1"),
    ("/test/file2.txt", "content2"),
    ("/file3.txt", "content3"),
    ("/test1/test2/test3/file5.txt", "content5"),
    ("/test2/file4.txt", "content4")
]

def deepupdate(original, update):
    for key, value in original.items():
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key])
    return update


def traverse(key, value):
    directory = os.path.dirname(key)
    filename = os.path.basename(key)
    if directory == "/":
        return value if isinstance(value, dict) else {filename: value}
    else:
        path, directory = os.path.split(directory)
        return traverse(path, {directory: {filename: value}})


result = {}
for key, value in paths:
    result = deepupdate(result, traverse(key, value))

print(result)

Using deepupdate() function suggested here.

It prints:

{'file3.txt': 'content3',
 'test': {'file1.txt': 'content1', 'file2.txt': 'content2'},
 'test1': {'test2': {'test3': {'file5.txt': 'content5'}}},
 'test2': {'file4.txt': 'content4'}}
Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195