【问题标题】:Adding items to list in dict if entry already exists如果条目已存在,则将项目添加到 dict 中的列表
【发布时间】:2020-10-12 02:30:55
【问题描述】:

我收到了许多包含不同产品订单的 CSV 文件。这些 CSV 文件需要“转换”为特定的 JSON 结构。

CSV 文件的每一行代表一个产品的订单。这意味着如果我要订购两种产品,CSV 将包含两行。

CSV 文件的简化版本可能如下所示(请注意第一行和第三行中的orderId“111”):

orderId,itemNumber,itemName,name,street
111,123,testitem,john doe,samplestreet 1
222,345,anothertestitem,jane doe,samplestreet 1
111,345,anothertestitem,john doe,samplestreet 1

我当前的解决方案有效,但我认为我过于复杂了。

目前,我正在遍历每个 CSV 行并创建 JSON 结构,其中我使用一个辅助函数,该函数将添加订单或附加一个包含有序项目的列表,如下所示:

def add_orderitem(orderitem, order, all_orders):
    """ Adds an ordered product to the order or "create" a new order if it doesn't exist """
    for row in all_orders:
        # Order already exists
        if any(order["orderNumber"] == value for field, value in row.items()):
            print(f"Order '{order['orderNumber']}' already exists, adding product #{orderitem['sku']}")
            row["orderItems"].append(orderitem)
            return all_orders

    # New order
    print(f"New Order found, creating order '{order['orderNumber']}' and adding product #{orderitem['sku']}")
    all_orders.append(order)
    order["orderItems"].append(orderitem)
    return all_orders


def parse_orders():
    """ Converts CSV-orders into JSON """
    results = []
    orders = read_csv("testorder.csv")  # helper-function returns CSV-dictreader (list of dicts)
    for order in orders:
        # Create basic structure
        orderdata = {
            "orderNumber": order["orderId"],
            "address": {
                "name": order["orderId"],
                "street": order["street"]
            },
            "orderItems": []  # <-- this will be filled later
        }

        # Extract product-information that will be inserted in above 'orderItems' list
        product = {
            "sku": order["itemNumber"],
            "name": order["itemName"]
        }

        # Add order to final list or add item if order already exists
        results = add_orderitem(product, orderdata, results)

    return results


def main():
    from pprint import pprint
    parsed_orders = parse_orders()

    pprint(parsed_orders)


if __name__ == "__main__":
    main()

脚本工作正常,下面的输出是我所期待的:

New Order found, creating order '111' and adding product #123
New Order found, creating order '222' and adding product #345
Order '111' already exists, adding product #345
[{'address': {'name': '111', 'street': 'samplestreet 1'},
  'orderItems': [{'name': 'testitem', 'sku': '123'},
                 {'name': 'anothertestitem', 'sku': '345'}],
  'orderNumber': '111'},
 {'address': {'name': '222', 'street': 'samplestreet 1'},
  'orderItems': [{'name': 'anothertestitem', 'sku': '345'}],
  'orderNumber': '222'}]

有没有办法“更聪明”地做到这一点?

【问题讨论】:

  • parse_orders 中,results 应该是defaultdict(list),以orderId 作为键。 (即results[order["orderId"]].append(orderData))。目前,add_orderitem 中的其他代码是多余的。
  • 感谢您的提示!不幸的是,这会“复制”所有信息并为每个订购的产品添加一个全新的字典。我需要的是只在“orderItems”列表中添加产品,不复制其余数据
  • 嗯,这让它变得更容易了。只需将results 设为普通的dict 并添加orderData,前提是orderId 不在results 中。然后你可以简单地做results[order['orderId']]['orderItems'].append(product)。这只需要更改 parse_orders 中的 4 行代码(不计算缩进),并摆脱其他功能。这似乎是迄今为止最简单的解决方案,并且它具有返回一个结构的额外优势,该结构提供免费的 order-id 查找。

标签: python list csv dictionary


【解决方案1】:

Imo namedtuplegroupby 会让你的代码更清晰:

from collections import namedtuple
from itertools import groupby

# csv data or file
data = """orderId,itemNumber,itemName,name,street
111,123,testitem,john doe,samplestreet 1
222,345,anothertestitem,jane doe,samplestreet 1
111,345,anothertestitem,john doe,samplestreet 1
"""

# the Order tuple
Order = namedtuple('Order', 'orderId itemNumber itemName name street')

# load the csv into orders
orders = [Order(*values) for line in data.split("\n")[1:] if line for values in [line.split(",")]]

# and group it by orderId
orders = sorted(orders, key = lambda order: order.orderId)

# group it by orderId
output = list()
for key, values in groupby(orders, key=lambda order: order.orderId):
    items = list(values)
    dct = {"address": {"name": items[0].name, "street": items[0].street},
           "orderItems": [{"name": item.itemName, "sku": item.itemNumber} for item in items]}
    output.append(dct)

print(output)

这会产生

[{'address': {'name': 'john doe', 'street': 'samplestreet 1'}, 'orderItems': [{'name': 'testitem', 'sku': '123'}, {'name': 'anothertestitem', 'sku': '345'}]},
 {'address': {'name': 'jane doe', 'street': 'samplestreet 1'}, 'orderItems': [{'name': 'anothertestitem', 'sku': '345'}]}]

你甚至可以把它放在一个很好的理解中,但这不会使它更具可读性。

【讨论】:

  • 这太棒了!感谢一百万简!我已经删除了您的 sortedgroupby sn-ps 并将其添加到我的脚本中,它就像一个魅力。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
  • 2021-07-02
  • 1970-01-01
  • 2017-01-31
  • 2022-09-30
  • 2019-11-23
  • 2013-08-07
相关资源
最近更新 更多