【问题标题】:Iterating over a dictionary to create a list遍历字典以创建列表
【发布时间】:2017-05-07 10:13:51
【问题描述】:

我在名为 favoriteColors 的 MongoDB 集合中有以下 4 个字典:

{ "name" : "Johnny", "color" : "green" }
{ "name" : "Steve", "color" : "blue" },
{ "name" : "Ben", "color" : "red" },
{ "name" : "Timmy", "color" : "cyan" }

我正在尝试创建与不同有序列表匹配的颜色值的有序列表。

例如,如果我有列表 ["Johnny", "Steve", "Ben", "Johnny"],则新列表将为 ["green", "blue", "red", "green"]

如果我有列表 ["Steve", "Steve", "Ben", "Ben", "Johnny"],那么新列表将是 ["blue", "blue", "red", "red", "green"]

使用 Python 和/或 PyMongo 有什么好的方法。这是我到目前为止所拥有的,但它无法识别重复项。

name_list = ["Steve", "Steve", "Ben", "Ben", "Johnny"]

color_list = []
for document in db.favoriteColors.aggregate([
    {"$match": {"name": {"$in": name_list }}},
    {"$project": {"color": 1}}
]):
    for k, v in document.iteritems():
        color_list.append(v)

print color_list
# ["blue", "red", "green"]

【问题讨论】:

  • 您能否创建'name' -> 'color' 的映射,然后使用该映射与name_list 来构造颜色列表?我对 mongodb 知之甚少,不知道这是否可以有效地完成,但似乎可以......

标签: python mongodb dictionary pymongo aggregation-framework


【解决方案1】:

实际上,我们可以使用带有客户端处理的聚合框架来有效地做到这一点。

import pymongo


client = pymongo.MongoClient()
db = client.test # Or whatever is your database
favoriteColors = db.favoriteColors
first_list = ['Johnny', 'Steve', 'Ben', 'Johnny']

cursor = favoriteColors.aggregate([
    {'$match': {'name': {'$in': first_list}}}, 
    {'$project': {'part': {'$map': {
        'input': first_list, 
        'as': 'inp', 
        'in': {
            '$cond': [
                {'$eq': [ '$$inp', '$name']}, 
                '$color', 
                None
            ]
        }
    }}}},
    {'$group': {'_id': None, 'data': {'$push': '$part'}}}
])

因为我们$group 为 None,所以我们的光标包含一个文档,我们可以使用 next 检索该文档。事实上,我们可以通过print(list(cursor)) 验证方式

>>> import pprint
>>> pprint.pprint(list(cursor))
[{'_id': None,
  'data': [['green', None, None, 'green'],
           [None, 'blue', None, None],
           [None, None, 'red', None]]}]

从这里开始,我们需要用zip解包文档中的“数据”字段,使用chain.from_iterable链接输入并过滤掉None的元素。

from itertools import chain

result = [item 
          for item in chain.from_iterable(zip(*next(cursor)['data']))
          if item is not None]

返回:

>>> result
['green', 'blue', 'red', 'green']

【讨论】:

  • 你能用足够多的查询分享你的答案基准吗?似乎很有效。谢谢。
  • 这似乎可行,但看起来很复杂。使用聚合框架必须有一种更简单的方法来做到这一点。为什么你需要"$project"下的所有这些条目
  • @jcmetz21 我不认为这很复杂,如果输出列表中的元素不需要以这个确切的顺序出现,我将添加解释和可能的另一种方法,但它是不会更简单
  • 元素需要按照准确的顺序排列,这样看起来不错。谢谢!
  • @jcmetz21 我的回答不只是看起来不错。这是最好的方法。在 MongoDB 3.4 中可能还有另一种方法可以做到这一点。
【解决方案2】:

您还可以从您当前的字典中创建一个新的dict,其中dict["name"] 的每个值都将与dict["color"] 值相关联。

例如:新的字典会是这样的:

{"Jhonny": "green", "Steve": "blue"}

并且您可以使用像下面的示例这样的函数,它接受许多参数并返回所需的列表(此外,如果输入列表中存在默认字典中不存在的任何名称,它会附加 None):

这是我的例子:

a = { "name" : "Johnny", "color" : "green" }
b = { "name" : "Steve", "color" : "blue" }
c = { "name" : "Ben", "color" : "red" }
d = { "name" : "Timmy", "color" : "cyan" }

my_list = ["Steve", "Steve", "Ben", "Ben", "Johnny"]

def iter_func(my_list = list, *args):
    ne = {k["name"]:k["color"] for k in args}
    return [ne[k] if k in ne.keys() else None for k in my_list]

输出:

print(iter_func(my_list, a,b,c,d))
>>> ['blue', 'blue', 'red', 'red', 'green']

None 值的示例:

a = { "name" : "Johnny", "color" : "green" }
b = { "name" : "Steve", "color" : "blue" }
c = { "name" : "Ben", "color" : "red" }
d = { "name" : "Timmy", "color" : "cyan" }

my_list = ["Steve", "Steve", "Alex", "Ben", "Ben", "Johnny", "Mark"]

def iter_func(my_list = list, *args):
    ne = {k["name"]:k["color"] for k in args}
    return [ne[k] if k in ne.keys() else None for k in my_list]

输出:

print(iter_func(my_list, a,b,c,d))
>>> ['blue', 'blue', None, 'red', 'red', 'green', None]

【讨论】:

  • 不是这样做的。如果您有 100 万个文档与您的查询相匹配怎么办?
  • OP 并没有像你说的那样说他的查询很大。但感谢您的评论。我会努力改进我的答案。
【解决方案3】:

如果数据集很小,您可以将字典合并成一个新的字典。

在 python3 中你可以这样做:

names = ["Steve", "Steve", "Ben", "Ben", "Johnny"]
favorites = {d["name"]: d["color"] for d in db.favoriteColors.find()}
colors = [favorites[name] for name in names]
print(colors)

更新

正如 styvane 所说,我忘记在 Collection 上调用 find 方法。答案已相应更新。

【讨论】:

  • 集合对象不可迭代。这将因漂亮的 TypeError 而失败
  • 如果字典在 python 中并且几乎没有数据可以迭代,这是一种干净的方法。
  • @jcmetz21 我看不出这是一种干净的方式db.favoriteColors 是一个 Collection 对象并且没有实现迭代器协议,所以这个查询将返回一个 TypeError,正如我所提到的。
  • 如果我将字典放入列表并遍历列表,它会起作用。但我同意如果我们从 MongoDB 中获取字典,这是一种糟糕的做法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-29
相关资源
最近更新 更多