Python实现下划线分隔列表按规则截取去重的算法求助
列表路径解析Python算法实现
需求背景
需要对以下划线分割的路径字符串列表做规则提取,原始待处理列表如下:
raw_list = ['PPPP_YYYY_ZZZZ_XXXX', 'PPPP_TOTO_TATA_TITI_TUTU', 'PPPP_TOTO_MMMM_TITI_TUTU', 'PPPP_TOTO_EHEH_TITI_TUTU', 'PPPP_TOTO_EHEH_OOOO_AAAAA', 'PPPP_TOTO_EHEH_IIII_SSSS_RRRR']
提取规则
- 默认提取每个元素按下划线分割的最后2段,格式为
PARENT_CHILD,例如元素PPPP_TOTO_TATA_TITI_TUTU初始提取结果为TITI_TUTU - 若提取结果存在重复,则所有重复项同步向上多取1段(加入
GRANDPARENT层级),重复该操作直到所有提取结果无重复 - 若某条路径向上取段后引入公共前缀节点(例如
EHEH),则所有包含该前缀节点的同分支路径,提取结果需统一带上该前缀节点,例如原本OOOO_AAAAA需调整为EHEH_OOOO_AAAAA
期望输出
['ZZZZ_XXXX', 'TATA_TITI_TUTU', 'MMMM_TITI_TUTU', 'EHEH_TITI_TUTU', 'EHEH_OOOO_AAAAA', 'EHEH_IIII_SSSS_RRRR']
原有代码问题
初始实现代码如下:
json_paths = ['PPPP_YYYY_ZZZZ_XXXX', 'PPPP_TOTO_TATA_TITI_TUTU', 'PPPP_TOTO_EHEH_TITI_TUTU', 'PPPP_TOTO_MMMM_TITI_TUTU', 'PPPP_TOTO_EHEH_OOOO_AAAAA'] cols_name = [] for path in json_paths: acc=2 col_name = '_'.join(path.split('_')[-acc:]) tmp = cols_name while col_name in tmp: acc += 1 idx = tmp.index(col_name) cols_name[idx] = '_'.join(json_paths[idx].split('_')[-acc:]) col_name = '_'.join(path.split('_')[-acc:]) tmp = ['_'.join(item.split('_')[-acc:]) for item in json_paths].pop() cols_name.append(col_name) print(cols_name.index(col_name), col_name) cols_name
原有代码存在3个核心问题:
- 未实现规则3要求的公共前缀节点统一追加逻辑,会导致同分支路径前缀缺失
- 临时列表
tmp赋值错误,pop()方法仅返回列表最后一个元素,无法用于全量重复校验 - 仅调整当前重复项的提取长度,未同步更新关联路径的提取段数,结果不符合规则要求
修正后完整实现
def parse_path_list(raw_paths): # 预拆分所有路径为段列表 path_parts = [p.split('_') for p in raw_paths] path_count = len(path_parts) # 初始化每个路径默认提取尾部2段 acc_list = [2] * path_count def get_extract(acc, parts): # 兼容提取长度超过路径总段数的边界情况 valid_acc = min(acc, len(parts)) return '_'.join(parts[-valid_acc:]) # 循环迭代调整直到所有结果符合规则 while True: changed = False # 获取当前所有路径的提取结果 current_extracts = [get_extract(acc_list[i], path_parts[i]) for i in range(path_count)] # 定位所有重复的提取值 seen = {} dup_values = set() for idx, ext in enumerate(current_extracts): if ext in seen: dup_values.add(ext) else: seen[ext] = idx # 处理重复项:所有重复项提取长度+1 for val in dup_values: for idx in range(path_count): if current_extracts[idx] == val: if acc_list[idx] < len(path_parts[idx]): acc_list[idx] += 1 changed = True # 拿到本次新增的前缀节点和对应层级位置 new_prefix_pos = len(path_parts[idx]) - acc_list[idx] new_prefix_node = path_parts[idx][new_prefix_pos] # 同步处理公共前缀:所有同分支路径统一带上该前缀 for other_idx in range(path_count): if other_idx == idx: continue other_parts = path_parts[other_idx] other_len = len(other_parts) # 校验其他路径相同层级是否为同一公共前缀节点 if new_prefix_pos < other_len and other_parts[new_prefix_pos] == new_prefix_node: required_acc = other_len - new_prefix_pos if acc_list[other_idx] < required_acc: acc_list[other_idx] = required_acc changed = True if not changed: break # 生成最终结果 return [get_extract(acc_list[i], path_parts[i]) for i in range(path_count)] # 功能测试 if __name__ == "__main__": test_list = ['PPPP_YYYY_ZZZZ_XXXX', 'PPPP_TOTO_TATA_TITI_TUTU', 'PPPP_TOTO_MMMM_TITI_TUTU', 'PPPP_TOTO_EHEH_TITI_TUTU', 'PPPP_TOTO_EHEH_OOOO_AAAAA', 'PPPP_TOTO_EHEH_IIII_SSSS_RRRR'] result = parse_path_list(test_list) print(result)
运行代码后输出结果与期望完全一致:
['ZZZZ_XXXX', 'TATA_TITI_TUTU', 'MMMM_TITI_TUTU', 'EHEH_TITI_TUTU', 'EHEH_OOOO_AAAAA', 'EHEH_IIII_SSSS_RRRR']
逻辑说明:通过维护每个路径的提取长度列表,循环处理重复值,每次给重复项增加提取长度时,自动校验同层级相同前缀的其他路径,同步更新这些路径的提取长度,直到没有任何调整、所有结果满足规则要求。
内容的提问来源于stack exchange,提问作者Nouna
相关产品推荐
相关产品推荐

