根据值修改字典

时间:2019-02-19 14:53:02

标签: python list dictionary

我遇到以下问题:我创建了一个字典,其中的键是ID(从0到N),值是一个或多个数字的列表。

D = dict()
D[0] = [1]
D[1] = [2]
D[2] = [0]

OR:

D = dict()
D[0] = [1, 2]
D[1] = [1, 2]
D[2] = [0]

当存储在字典中的列表具有多个值时,始终表示此列表位于2个不同的键下。我现在想要的是将两个字典都转换成这样:

D = dict()
D[0] = 1
D[1] = 2
D[2] = 0

对于第一个来说,很简单,该函数将用列表中的第一个值替换dict的值:

def transform_dict(D):
    for key, value in D.items():
        D[key] = value[0]
    return D

但是,在第二种情况下,该函数必须为其中一个键分配一个值,而第二个键分配另一个值。例如,可以为键“ 0”分配值“ 1”或“ 2”;键“ 1”将分配给另一个。

我正在为这个简单的问题而苦苦挣扎,但我没有找到有效执行此操作的方法。你有什么主意吗?

编辑:解释n°2

初始字典可以具有以下格式:

D[key1] = [val1]
D[key2] = [val2]
D[key3] = [val3, val4]
D[key4] = [val3, val4]

如果值列表由一个以上的元素组成,则意味着字典中存在另一个具有相同值列表(键3和键4)的键。

目标是将该字典转换为:

D[key1] = val1
D[key2] = val2
D[key3] = val3
D[key4] = val4

其中val3和val4以任何方式归属于key3和key4(我不在乎哪一个与哪个键一起使用)。

EDIT2:示例:

# Input dict
D[0] = [7]
D[1] = [5]
D[2] = [4]
D[3] = [1, 2, 3]
D[4] = [6, 8]
D[5] = [1, 2, 3]
D[6] = [1, 2, 3]
D[7] = [6, 8]

#Output
D[0] = 7
D[1] = 5
D[2] = 4
D[3] = 1
D[4] = 6
D[5] = 2
D[6] = 3
D[7] = 8

2 个答案:

答案 0 :(得分:2)

您还可以创建行为类似于字典的类。这样,您不需要任何其他功能即可“清理”字典,而可以即时解决它:)

工作原理:

  • 我们扩展DECLARE @b1 varbinary(16) = convert(varbinary(16), newid()), @b2 varbinary(16) = convert(varbinary(16), newid()) SELECT CASE WHEN @b1 > @b2 THEN '@b1 is bigger' ELSE '@b2 is bigger' END 并覆盖标准字典函数collections.abc.Mapping__getitem____setitem__。我们使用__iter__保存实际的字典。

  • 我们使用第二个字典self._storage来跟踪尚未解析的键。在上面的示例中,它例如具有条目_unresolved

  • 我们使用辅助功能(1, 2, 3): [4, 5]来检查是否_resolve()。在您分配len((1,2,3)) == len([4,5])时,此长度相等,并且项目也分配给D[6]

试图在代码中添加注释。

self._storage

然后开始:

from collections.abc import Mapping
from collections import defaultdict 

class WeirdDict(Mapping):
    def __init__(self, *args, **kw):
        self._storage = dict()  # the actual dictionary returned

        self._unresolved = defaultdict(list)  # a reversed mapping of the unresolved items
        for key, value in dict(*args, **kw).items():
            self._unresolved_vals[value].append(key)

        self._resolve()

    def __getitem__(self, key):
        return self._storage[key]

    def __setitem__(self, key, val):
        """ Setter. """
        if type(val) == int:
            self._storage[key] = val 
        elif len(val) == 1:
            self._storage[key] = val[0]
        elif key not in self._storage:
            self._unresolved[tuple(val)].append(key)
            self._resolve()

    def _resolve(self):
        """ Helper function - checks if any keys can be resolved """
        resolved = set()
        for val, keys in self._unresolved.items():  # left to resolve
            if len(val) == len(keys):  # if we can resolve (count exhausted)
                for i, k in enumerate(keys):
                    self._storage[k] = val[i]
                    resolved.add(val)
        # Remove from todo list              
        for val in resolved:
            del self._unresolved[val]

    def __iter__(self):
        return iter(self._storage)

    def __len__(self):
        return len(self._storage)

答案 1 :(得分:1)

我不确定这是否是最有效的方法,但这似乎是一种方法:

in_dict = dict()
in_dict[0] = [7]
in_dict[1] = [5]
in_dict[2] = [4]
in_dict[3] = [1, 2, 3]
in_dict[4] = [6, 8]
in_dict[5] = [1, 2, 3]
in_dict[6] = [1, 2, 3]
in_dict[7] = [6, 8]

out_dict = dict()
out_dict[0] = 7
out_dict[1] = 5
out_dict[2] = 4
out_dict[3] = 1
out_dict[4] = 6
out_dict[5] = 2
out_dict[6] = 3
out_dict[7] = 8

def weird_process(mapping):
    result = dict()
    for key, val in mapping.items():
        if len(val) == 1:
            result[key] = val[0]
        elif key not in result:  # was: `else:`
            # find other keys having the same value
            matching_keys = [k for k, v in mapping.items() if v == val]
            for i, k in enumerate(matching_keys):
                result[k] = val[i]
    return result


weird_process(in_dict) == out_dict
# True

编辑:我对代码进行了一些简化。

EDIT2:通过跳过已处理的元素,我提高了效率


EDIT3

一种更快的方法是使用输入键的临时副本,通过在使用完输入后立即使用输入来减少内部循环:

def weird_process(mapping):
    unseen = set(mapping.keys())
    result = dict()
    for key, val in mapping.items():
        if len(val) == 1:
            result[key] = val[0]
        elif key not in result:
            # find other keys having the same value
            matching_keys = [k for k in unseen if mapping[k] == val]
            for i, k in enumerate(matching_keys):
                result[k] = val[i]
                unseen.remove(k)
    return result