将嵌套字典的键替换为另一个字典的值(两个字典的键相等)),键的值可以是dicts列表

时间:2016-01-10 14:17:45

标签: python dictionary

我有两个dict,dict1和dict2,我想用键值对构造新的dict(或操作dict1)(dict2的值:dict1的值[其中dict的键和dict2的键相同] ),键的值可以是dicts列表(正如您将在输入示例中看到的那样)

INPUT IS

dict1 = {"key1":{"key3":"value1","key2":"value2","key4":{"key5":"value3","key6":{"key7":"value4","key8":{"key9":"value5","key10":"value6","key55":"value7"}},"key11":{"key12":"value8","key13":"value9"},"key14":[{"key15":"value10","key16":"value11","key17":"value12"},{"key15":"value13","key16":"value14","key17":"value15"}]}}}

dict2 = {"key1":"ab","key2":"bc","key3":"cd","key4":"de","key5":"ef","key6":"fg","key7":"gh","key8":"hi","key9":"ij","key10":"jk","key55":"kl","key11":"lm","key12":"mn","key13":"no","key14":"op","key15":"pq","key16":"qr","key17":"qs"}

我的功能是

def walk(dict1, dict2):
    output = {}
    for key, value in dict1.iteritems():

        if isinstance(value, dict):
            output[dict2[key]] = walk(value, dict2)
        elif isinstance(value, list):
            output[dict2[key]] = walk_list(value, dict2)
        else:
            output[dict2[key]] = value
    return output

def walk_list(sublist, dict2):
    output = []
    for i in sublist:

        if isinstance(i, dict):
            output = walk(i, dict2)
        elif isinstance(value, list):
            output = walk_list(i, dict2)
        else:
            output.append((key, value))
    return output

output = walk(dict1, dict2)
output = json.dumps(output)
print output

输出结束

 {"ab": {"de": {"lm": {"mn": "value8", "no": "value9"}, "ef": "value3", "fg": {"hi": {"ij": "value5", "jk": "value6", "kl": "value7"}, "gh": "value4"}, "op": {"pq": "value13", "qs": "value15", "qr": "value14"}}, "bc": "value2", "cd": "value1"}}

预期输出

 {"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}

请修复我的代码。

3 个答案:

答案 0 :(得分:3)

非常简单的解决方案,它很早就执行递归步骤,因此具有非常简单的逻辑:

def translateKeys (obj, keyNames):
    if isinstance(obj, dict):
        return {keyNames.get(k, k): translateKeys(v, keyNames) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [translateKeys(v, keyNames) for v in obj]
    else:
        return obj

不是期望某种类型,而是接受任何东西(字典,列表或其他)并对其项目进行处理,为每个值调用自己。这避免了必须遍历obj本身并检查循环中每个项目的值。

用于示例数据:

>>> dict1 = {"key1":{"key3":"value1","key2":"value2","key4":{"key5":"value3","key6":{"key7":"value4","key8":{"key9":"value5","key10":"value6","key55":"value7"}},"key11":{"key12":"value8","key13":"value9"},"key14":[{"key15":"value10","key16":"value11","key17":"value12"},{"key15":"value13","key16":"value14","key17":"value15"}]}}}
>>> dict2 = {"key1":"ab","key2":"bc","key3":"cd","key4":"de","key5":"ef","key6":"fg","key7":"gh","key8":"hi","key9":"ij","key10":"jk","key55":"kl","key11":"lm","key12":"mn","key13":"no","key14":"op","key15":"pq","key16":"qr","key17":"qs"}
>>> expected =  {"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}
>>> result = translateKeys(dict1, dict2)
>>> result
{'ab': {'de': {'fg': {'gh': 'value4', 'hi': {'ij': 'value5', 'jk': 'value6', 'kl': 'value7'}}, 'op': [{'qr': 'value11', 'pq': 'value10', 'qs': 'value12'}, {'qr': 'value14', 'pq': 'value13', 'qs': 'value15'}], 'ef': 'value3', 'lm': {'no': 'value9', 'mn': 'value8'}}, 'cd': 'value1', 'bc': 'value2'}}
>>> result == expected
True

如果您要反转此翻译,您只需反转keyNames并在结果上执行翻译:

>>> result = translateKeys(dict1, dict2)
>>> invertedKeyNames = {v: k for k, v in dict2.items()}
>>> original = translateKeys(result, invertedKeyNames)
>>> original == dict1
True

答案 1 :(得分:1)

我认为这只用一个函数来解决这个谜题

def walk(dict1, dict2):
    res = dict()
    for k,v in dict1.items():
        if isinstance(v,list):
            newv = [walk(x, dict2) for x in v]
        elif isinstance(v,dict):
            newv = walk(v, dict2)
        else:
            newv = v
        res[dict2.get(k, k)] = newv # keep the same key if not present in dict2
    return res

expected = {"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}

output = walk(dict1, dict2)
print(output)
print(output == expected)

生成

{'ab': {'de': {'lm': {'no': 'value9', 'mn': 'value8'}, 'ef': 'value3', 'fg': {'hi': {'ij': 'value5', 'kl': 'value7', 'jk': 'value6'}, 'gh': 'value4'}, 'op': [{'qr': 'value11', 'qs': 'value12', 'pq': 'value10'}, {'qr': 'value14', 'qs': 'value15', 'pq': 'value13'}]}, 'cd': 'value1', 'bc': 'value2'}}
True

基本上它会检查字典中的每个值:

  • 如果是值列表,则将其自身应用于列表中的每个项目
  • 如果是字典,它就适用于它
  • 如果它是字面值,则使用它

编辑:

如果输入词典不仅仅是字典,而且可以是任何可以变得更通用的可接受的json元素(例如list,dict,value)

def walk(obj, keys):
    if isinstance(obj,list):
        return [walk(x, keys) for x in obj]
    elif isinstance(obj,dict):
        return {keys.get(k, k): walk(v, keys) for k,v in obj.items()}
    else:
        return obj

这正是@Poke从一开始就回答的问题,对他赞不绝口。

EDIT2:

如果你想恢复原始字典dict1,如果值都是不相交的(即dict2映射是bijective function),你可以做

back2dict1 = walk(output, {v:k for k,v in dict2.items()})
print(back2dict1)
print(back2dict1 == dict1)

产生

{'key1': {'key3': 'value1', 'key2': 'value2', 'key4': {'key5': 'value3', 'key11': {'key12': 'value8', 'key13': 'value9'}, 'key14': [{'key15': 'value10', 'key16': 'value11', 'key17': 'value12'}, {'key15': 'value13', 'key16': 'value14', 'key17': 'value15'}], 'key6': {'key7': 'value4', 'key8': {'key10': 'value6', 'key55': 'value7', 'key9': 'value5'}}}}}
True

答案 2 :(得分:1)

我认为这与你的walk_list函数有关,输出变量被赋值而不是附加到。这是我的版本:

dict1 = {"key1":{"key3":"value1","key2":"value2","key4":{"key5":"value3","key6":{"key7":"value4","key8":{"key9":"value5","key10":"value6","key55":"value7"}},"key11":{"key12":"value8","key13":"value9"},"key14":[{"key15":"value10","key16":"value11","key17":"value12"},{"key15":"value13","key16":"value14","key17":"value15"}]}}}
dict2 = {"key1":"ab","key2":"bc","key3":"cd","key4":"de","key5":"ef","key6":"fg","key7":"gh","key8":"hi","key9":"ij","key10":"jk","key55":"kl","key11":"lm","key12":"mn","key13":"no","key14":"op","key15":"pq","key16":"qr","key17":"qs"}

def walk(dict1, dict2):
    output = {}
    for key, value in dict1.iteritems():

        if isinstance(value, dict):
            outVal = walk(value, dict2)
        elif isinstance(value, list):
            outVal = walk_list(value, dict2)
        else:
            outVal = value
        output[dict2[key]] = outVal

    return output

def walk_list(sublist, dict2):
    output = []
    for i in sublist:
        if isinstance(i, dict):
            outVal = walk(i, dict2)
        elif isinstance(i, list):
            outVal = walk_list(i, dict2)
        else:
            outVal = i
        output.append(outVal)

    return output            

mine = walk(dict1, dict2)
expecting = {"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}

print mine == expecting