【问题标题】:Replace keys of a nested dictionary with the value of another dictionary(where keys of two dict are equal)), value for a key may be list of dicts用另一个字典的值替换嵌套字典的键(其中两个字典的键相等)),键的值可能是字典列表
【发布时间】:2016-04-14 20:53:26
【问题描述】:

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

输入是

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"}]}}}

请修正我的代码。

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

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

    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
    

    【讨论】:

    • 它是通用的吗?意味着它适用于任何 JSON 吗?键的类似值可以是简单的值、列表、字典根据 JSON 结构,列表和字典的任何其他嵌套有效组合?
    • 是的,它应该与 obj 一起使用,这是任何类型的反序列化 JSON 对象。由于JSON objects只能是对象(=字典)、数组(=列表)或值(数字、字符串、truefalsenull),我们处理对象和数组的递归(因为这些是唯一可以包含其他可能需要密钥转换的 JSON 对象的类型),所以它应该适用于来自 JSON 的任何有效对象。
    • 能否提供另外一个函数来使用dict2和result来检索dict1?
    • 当然,你只需要反转dict2 字典,所以映射在另一个方向,然后将其应用于先前的结果。请参阅我编辑的答案。
    【解决方案2】:

    我认为这与您的 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
    

    【讨论】:

    • 它是通用的吗?意味着它适用于任何 JSON 吗?键的类似值可以是简单的值、列表、字典根据 JSON 结构,列表和字典的任何其他嵌套有效组合?
    • 是的,我相信是的。 :)
    • 顺便说一句,感谢您的输入,您能否提供另一个函数来使用 dict2 和 result 检索 dict1?
    【解决方案3】:

    我认为这仅使用一个功能即可解决谜题

    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 元素(例如列表、字典、值),可以变得更加通用

    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
    

    【讨论】:

    • res[dict2[k]] = newv 应该是res[dict2.get(k, k)] = newv 来处理k not in dict2 时的情况。
    • @DanD。是的,谢谢你指出这一点。想了想,最后没有写下来
    • 它是通用的吗?意味着它适用于任何 JSON 吗?键的类似值可以是简单的值、列表、字典根据 JSON 结构,列表和字典的任何其他嵌套有效组合?
    • 顺便说一句,您可能需要更新问题的标题和描述以更准确地反映您的要求。它将帮助未来的读者解决同样的问题
    • 顺便说一句,感谢您的输入,您能否提供另一个函数来使用 dict2 和 result 检索 dict1?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多