Substitute list of paths with unique elements at every level (/)

Viewed 41

I have a list of path strings ( which can form a tree structure ) like below:

xo =    ['1/sometext1',
     '1/1/sometext2',
     '1/1/1/sometext3',
     '1/1/2/sometext4',
     '1/2/sometext5',
     '1/2/1/sometext6',
     '1/2/2/sometext7',
     '2/sometext8',
     '3/sometext9']

I want to convert above list into a form like below with unique numbers specific to every level. So that there will be proper differentiation between 1's in ('1/', '1/1/','1/1/1/') and 2's in ('1/1/2/','1/2/','1/2/1/','1/2/2/','2/').

xd = ['123/sometext1',
 '123/1234/sometext2',
 '123/1234/12345/sometext3',
 '123/1234/234/sometext4',
 '123/2345/sometext5',
 '123/2345/123456/sometext6',
 '123/2345/23456/sometext7',
 '234567/sometext8',
 '3456/sometext9']

The unique values are just for example and can be any unique strings.

2 Answers

This example will add depth number to each level:

import re

xo = [
    "1/sometext1",
    "1/1/sometext2",
    "1/1/1/sometext3",
    "1/1/2/sometext4",
    "1/2/sometext5",
    "1/2/1/sometext6",
    "1/2/2/sometext7",
    "2/sometext8",
    "3/sometext9",
]

pat = re.compile(r"((?:\d+/)+)(.*)")

out = []
for s in xo:
    nums, rest = pat.match(s).groups()
    nums = "/".join(f"{i}-{n}" for i, n in enumerate(nums.split("/"), 1) if n)
    out.append(nums + "/" + rest)

print(out)

Prints:

[
    "1-1/sometext1",
    "1-1/2-1/sometext2",
    "1-1/2-1/3-1/sometext3",
    "1-1/2-1/3-2/sometext4",
    "1-1/2-2/sometext5",
    "1-1/2-2/3-1/sometext6",
    "1-1/2-2/3-2/sometext7",
    "1-2/sometext8",
    "1-3/sometext9",
]

EDIT: Modified example:

import re

xo = [
    "1/sometext1",
    "1/1/sometext2",
    "1/1/1/sometext3",
    "1/1/2/sometext4",
    "1/2/sometext5",
    "1/2/1/sometext6",
    "1/2/2/sometext7",
    "2/sometext8",
    "3/sometext9",
]

pat = re.compile(r"((?:\d+/)+)(.*)")

out = []
for s in xo:
    nums, rest = pat.match(s).groups()
    tmp = [n for n in nums.split("/") if n]
    nums = "/".join(f"{'.'.join(tmp[:i])}" for i in range(1, len(tmp) + 1))
    out.append(nums + "/" + rest)

print(out)

Prints:

[
    "1/sometext1",
    "1/1.1/sometext2",
    "1/1.1/1.1.1/sometext3",
    "1/1.1/1.1.2/sometext4",
    "1/1.2/sometext5",
    "1/1.2/1.2.1/sometext6",
    "1/1.2/1.2.2/sometext7",
    "2/sometext8",
    "3/sometext9",
]

This code below will, for every path component, generate a unique corresponding number for that specific value:

from collections import defaultdict
import random, string
class UniquePaths:
   def __init__(self):
      self.paths = []
   def new_path(self):
      while (p:=''.join(random.choice(string.digits) for _ in range(random.randint(3, 10)))) in self.paths:
          pass
      self.paths.append(p)
      return p
   def build_results(self, d, new_p = []):
      _d = defaultdict(list)
      for i in d:
         if len(i) == 1:
            yield '/'.join(new_p)+'/'+i[0]
         else:
            _d[i[0]].append([*i[1:-1], i[-1]])
      yield from [j for b in _d.values() for j in self.build_results(b, new_p+[self.new_path()])]
   @classmethod
   def to_unique(cls, paths):
       return list(cls().build_results([i.split('/') for i in paths]))
      
xo = ['1/sometext1', '1/1/sometext2', '1/1/1/sometext3', '1/1/2/sometext4', '1/2/sometext5', '1/2/1/sometext6', '1/2/2/sometext7', '2/sometext8', '3/sometext9']
new_paths = UniquePaths.to_unique(xo)

Output:

['987498/sometext1', 
 '987498/3886405008/sometext2', 
 '987498/3886405008/4380239/sometext3', 
 '987498/3886405008/0407507/sometext4', 
 '987498/984618899/sometext5', 
 '987498/984618899/89110/sometext6', 
 '987498/984618899/45767633/sometext7', 
 '50264/sometext8', 
 '768/sometext9']

The solution above does not base the unique value generation on the original component values themselves, thus removing any possibility of producing a non unique path component, and instead randomly generates strings of varying lengths.

Related