将元组列表映射为字典

5
我有一个元组列表,从数据库中提取而来,看起来像这样(keyforeignkeyvalue)。键和外键之间存在多对一关系,我希望将其转换为由外键索引的字典,其中包含具有该外键的所有值的总和,即{foreignkeysumof(value )}。我编写了一些冗长的代码:
myDict = {}
for item in myTupleList:
    if item[1] in myDict:
        myDict [ item[1] ] += item[2]
    else:
        myDict [ item[1] ] = item[2]

但是在看了这个问题的答案或者这两个问题的回答之后,我发现肯定有更简洁的方法来表达我想做的事情。如果这是一个重复的问题,我会删除它并提供链接。


1
也许有些偏题,但我还是要问一下...为什么不在数据库层面上完成这个操作呢?这正是DBMS擅长的事情... - mjv
1
实际上它并不是一个数据库,而是通过一个将其视为数据库的接口进行访问。它只是一堆扁平文件。 - wheaties
5个回答

10
假设你的所有值都是整数,你可以使用一个defaultdict来使这个过程更容易:
from collections import defaultdict

myDict = defaultdict(int)

for item in myTupleList:
    myDict[item[1]] += item[2]

defaultdict类似于一个字典,但如果你尝试获取一个不存在的键,则会填充由可调用函数返回的值 - 在这种情况下,int返回没有参数时的0。

更新:感谢@gnibbler提醒我,但元组可以在for循环中解包:

from collections import defaultdict

myDict = defaultdict(int)

for _, key, val in myTupleList:
    myDict[key] += val

在这里,3个元素的元组被拆分成变量_keyval。在Python中,_是一个常见的占位符名称,用于指示该值并不重要。使用它,我们可以避免复杂的item[1]item[2]索引。如果myTupleList中的元组大小不一致,我们就不能依赖它,但我敢打赌它们是一样的。
(我们还避免了某人查看代码并认为它已经损坏的情况,因为作者认为数组是从1开始索引的,这也是当我第一次阅读代码时的想法。直到我看到这个问题才消除了我的疑虑。然而,在上面的循环中,显然myTupleList是一个包含三个元素的元组,我们只需要后两个元素。)

1
我的字典 = reduce(lambda d, t: (d[t[1]] += t[2], d)[1], myTupleList, defaultdict(int)) - 你知道的,每个人都应该使用reduce!大笑 - Omnifarious
Omnifarious,您可以发布它作为答案并加以解释吗?那看起来非常有趣。我正在寻找任何能教会我新东西的东西,特别是函数式的答案。 - wheaties
@wheaties,好的,既然你问了。 不过我更喜欢Chris的解决方案。在这种情况下,reduce会让人难以理解。而且我的解决方案还稍微有点问题,但我会发布正确的版本。 - Omnifarious

5
from collections import defaultdict

myDict = defaultdict(int)

for _, key, value in myTupleList:
    myDict[key] += value

元组列表中有三个值,第一个值没有被使用。但是简单的解决方法是在“key”之前加上“_”,所以+1。 - Chris Lutz
这样做不行,因为myTupleList是一个包含三个元素的元组。你会得到ValueError: too many values to unpack的错误。 - Troy J. Farrell
@Chris Lutz,如果你修复了它,我不会介意... :) - John La Rooy
@gnibbler - 有些人如果别人编辑了他们的答案会有点不高兴,但还是谢谢。 - Chris Lutz
我认为这个版本比克里斯的原始版本更有效率一些。 - Omnifarious
我也非常喜欢这个答案。非常简洁明了,表达的意思很清晰。 - wheaties

4
这是我的(开玩笑的)回答:
myDict = reduce(lambda d, t: (d.__setitem__(t[1], d.get(t[1], 0) + t[2]), d)[1], myTupleList, {})

它看起来很丑很差,但是它的工作原理如下。

reduce的第一个参数(因为不清楚)是lambda d, t: (d.__setitem__(t[1], d.get(t[1], 0) + t[2]), d)[1]。稍后我会详细讲解,但现在,我只会称其为joe(无意冒犯任何名叫Joe的人)。reduce函数基本上是这样工作的:

 joe(joe(joe({}, myTupleList[0]), myTupleList[1]), myTupleList[2])

这是一个三元素列表。可以看到,它基本上使用第一个参数将每个结果累积到最终答案中。在这种情况下,最终答案就是您想要的字典。

现在来看joe本身。这是joedef

def joe(myDict, tupleItem):
   myDict[tupleItem[1]] = myDict.get(tupleItem[1], 0) + tupleItem[2]
   return myDict

很遗憾,在Python的lambda中不允许使用任何形式的=return,因此必须绕过这个问题。我通过直接调用dict__setitem__函数来解决缺少=的问题。我通过创建一个包含__setitem__返回值和字典的元组,然后返回包含字典的元素的元组来解决缺少返回值的问题。我将逐步修改joe,以便您可以看到我是如何完成这个任务的。

首先,删除=

def joe(myDict, tupleItem):
   # Using __setitem__ to avoid using '='
   myDict.__setitem__(tupleItem[1], myDict.get(tupleItem[1], 0) + tupleItem[2])
   return myDict

接下来,让整个表达式计算出我们想要返回的值:
def joe(myDict, tupleItem):
   return (myDict.__setitem__(tupleItem[1], myDict.get(tupleItem[1], 0) + tupleItem[2]),
           myDict)[1]

在我的Python编程中,我经常遇到使用reducedict的情况。 我认为,dict可以使用成员函数reduceto(keyfunc, reduce_func, iterable, default_val=None)keyfunc将从可迭代对象中获取当前值并返回键。 reduce_func将获取字典中的现有值和可迭代对象中的值,并返回字典的新值。 如果字典缺少键,则default_val将传递给reduce_func。 返回值应该是字典本身,因此您可以执行以下操作:

myDict = dict().reduceto(lambda t: t[1], lambda o, t: o + t, myTupleList, 0)

谢谢您的发布。虽然它很丑陋且难以理解,但至少具有教育意义。我非常喜欢列表推导式,希望能够找到某种可以利用的字典推导式。我选择了他的答案。它已经尽可能地简洁了。 - wheaties
@wheaties,Python的新版本(虽然我不确定它们需要多新)确实有字典推导式。我不确定您是否可以按照您想要的方式在此处使用它们。我更新了我的答案,添加了一个很好的函数,可以添加到“dict”接口中,使处理这种情况变得更加容易。 - Omnifarious
Python 3拥有字典推导式。请参阅http://docs.python.org/dev/3.0/whatsnew/3.0.html。 - Corey Goldberg
@Corey Goldberg,谢谢,我以为这可能是Py3k的问题。正如我所怀疑的那样,它们不能帮助您高效地解决此问题。或许简洁明了,但并不高效,因为没有好的方法来按字典键收集结果。 - Omnifarious

0

可能不是特别易读,但它应该可以工作:

fks = dict([ (v[1], True) for v in myTupleList ]).keys()
myDict = dict([ (fk, sum([ v[2] for v in myTupleList if v[1] == fk ])) for fk in fks ])

第一行代码查找所有唯一的外键。第二行代码通过首先构建一个(fk,sum(此fk的所有值))对列表,然后将其转换为字典来构建您的字典。

0

看看SQLAlchemy,看它是否提供了你所需的所有映射,甚至更多


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接