将循环算法与最后一条记录进行比较?

Generalize algorithm for a loop comparing to last record?

我有一个数据集,可以用这个字典列表的玩具示例表示:

data = [{
        "_id" : "001",
        "Location" : "NY",
        "start_date" : "2022-01-01T00:00:00Z",
        "Foo" : "fruits"
    },
        {
        "_id" : "002",
        "Location" : "NY",
        "start_date" : "2022-01-02T00:00:00Z",
        "Foo" : "fruits"
    },
    {
        "_id" : "011",
        "Location" : "NY",
        "start_date" : "2022-02-01T00:00:00Z",
        "Bar" : "vegetables"
    },
        {
        "_id" : "012",
        "Location" : "NY",
        "Start_Date" : "2022-02-02T00:00:00Z",
        "Bar" : "vegetables"
    },
    {
        "_id" : "101",
        "Location" : "NY",
        "Start_Date" : "2022-03-01T00:00:00Z",
        "Baz" : "pizza"
    },
        {
        "_id" : "102",
        "Location" : "NY",
        "Start_Date" : "2022-03-2T00:00:00Z",
        "Baz" : "pizza"
    },
]

这是 Python 中的一个算法,它收集每个 'collection' 中的每个键,只要有键更改,算法就会将这些键添加到输出中。

data_keys = []
for i, lst in enumerate(data):
    all_keys = []
    for k, v in lst.items():
        all_keys.append(k)
        if k.lower() == 'start_date':
            start_date = v
    this_coll = {'start_date': start_date, 'all_keys': all_keys}
    if i == 0:
        data_keys.append(this_coll)
    else:
        last_coll = data_keys[-1]
        if this_coll['all_keys'] == last_coll['all_keys']:
            continue
        else:
            data_keys.append(this_coll)

此处给出的正确输出记录了字段名称的每次更改:FooBarBaz以及字段start_date中的大小写更改为Start_Date:

[{'start_date': '2022-01-01T00:00:00Z',
  'all_keys': ['_id', 'Location', 'start_date', 'Foo']},
 {'start_date': '2022-02-01T00:00:00Z',
  'all_keys': ['_id', 'Location', 'start_date', 'Bar']},
 {'start_date': '2022-02-02T00:00:00Z',
  'all_keys': ['_id', 'Location', 'Start_Date', 'Bar']},
 {'start_date': '2022-03-01T00:00:00Z',
  'all_keys': ['_id', 'Location', 'Start_Date', 'Baz']}]

是否有涵盖此模式的通用算法将当前项与堆栈中的前一项进行比较?

我需要概括这个算法并找到一个解决方案来对集合中的 MongoDB 文档做完全相同的事情。为了让我发现 Mongo 是否有一个我可以使用的聚合管道运算符,我必须首先了解这个基本算法是否有其他常见形式,以便我知道要寻找什么。

或者非常了解 MongoDB 聚合管道的人可以建议可以产生所需结果的运算符?

itertools.groupby 在键值改变时迭代子迭代器。它会为您跟踪更改的密钥。在您的情况下,这是字典的键。您可以创建一个列表理解,从每个子迭代器中获取第一个值。

import itertools

data = ... your data ...
data_keys = [next(val) 
    for _, val in itertools.groupby(data, lambda record: record.keys())]
for row in data_keys:
    print(row)

结果

{'_id': '001', 'Location': 'NY', 'start_date': '2022-01-01T00:00:00Z', 'Foo': 'fruits'}
{'_id': '011', 'Location': 'NY', 'start_date': '2022-02-01T00:00:00Z', 'Bar': 'vegetables'}
{'_id': '012', 'Location': 'NY', 'Start_Date': '2022-02-02T00:00:00Z', 'Bar': 'vegetables'}
{'_id': '101', 'Location': 'NY', 'Start_Date': '2022-03-01T00:00:00Z', 'Baz': 'pizza'}

编辑:如果你想为此使用查询,一个选项是这样的:

  1. $objectToArray允许将键格式化为值,$ifNull允许检查start_date的几个选项。
  2. $unwind 允许我们对键进行排序。
  3. $group 允许我们撤消 $unwind,但现在使用排序键
  4. $reduce 从所有键创建一个字符串,这样我们就有东西可以比较了。
  5. 再次分组,但现在使用我们的字符串,因此我们将只有更改的文档。
db.collection.aggregate([
  {
    $project: {
      data: {$objectToArray: "$$ROOT"},
      start_date: {$ifNull: ["$start_date", "$Start_Date"]}
    }
  },
  {$unwind: "$data"},
  {$project: {start_date: 1, key: "$data.k", _id: 0}},
  {$sort: {start_date: 1,  key: 1}},
  {$group: {_id: "$start_date", all_keys: {$push: "$key"}}},
  {
    $project: {
      all_keys: 1,
      all_keys_string: {
        $reduce: {
          input: "$all_keys",
          initialValue: "",
          in: {$concat: ["$$value", "$$this"]}
        }
      }
    }
  },
  {
    $group: {
      _id: "$all_keys_string",
      all_keys: {$first: "$all_keys"},
      start_date: {$first: "$_id"}
    }
  },
  {$unset: "_id"}
])

Playground example