【问题标题】:How can I create aggregate expressions of this list of dicts?如何创建此字典列表的聚合表达式?
【发布时间】:2017-05-12 14:57:24
【问题描述】:

我有一个字典列表,表示学生信息系统中某个班级的时段+天数。这是我要汇总的数据:

[
    {
        'period': {
            'name': '1',
            'sort_order': 1
        },
        'day': {
            'name': 'A',
            'sort_order': 1
        }
    },
    {
        'period': {
            'name': '1',
            'sort_order': 1
        },
        'day': {
            'name': 'B',
            'sort_order': 2
        }
    },
    {
        'period': {
            'name': '1',
            'sort_order': 1
        },
        'day': {
            'name': 'C',
            'sort_order': 1
        }
    },
    {
        'period': {
            'name': '3',
            'sort_order': 3
        },
        'day': {
            'name': 'A',
            'sort_order': 1
        }
    },
    {
        'period': {
            'name': '3',
            'sort_order': 3
        },
        'day': {
            'name': 'B',
            'sort_order': 2
        }
    },
    {
        'period': {
            'name': '3',
            'sort_order': 3
        },
        'day': {
            'name': 'C',
            'sort_order': 2
        }
    },
    {
        'period': {
            'name': '4',
            'sort_order': 4
        },
        'day': {
            'name': 'D',
            'sort_order': 3
        }
    }
]

我希望将上面的聚合字符串简化为1,3(A-C) 4(D)。请注意,彼此不“相邻”(由对象的sort_order 确定)的对象由, 分隔,而“相邻”记录由- 分隔。

编辑

让我尝试详细说明聚合过程。每个“班级会议”对象都包含一个时期和日期。通常每天大约有 5 个时段,并且这些日子在 A、B、C、D 等之间循环交替。因此,如果我有一个课程在 A 日的第一个时段出现,我们可以将其表示为 1(A)。如果在 A 天的第 1 和第 2 课时上课,其原始形式可能是 1(A),2(A),但可以缩短为 1-2(A)。

某些课程可能不在“相邻”时段或天数中。 A 日的第 1 期和第 3 期可能会出现一堂课,因此其缩写形式为 1,3(A)。但是,如果该课程在 A 日的第 1、第 2 和第 3 节课上,则可以写为 1-3(A)。这也适用于日期,因此如果课程在 A、B 和 C 日的第 1、2 和 3 期发生,那么我们可以将其写为 1-3(A-C)。

最后,如果一个类出现在第 1、2、3 个时段以及 A、B 和 C 日,但也出现在 D 日的第 4 个时段,则其缩写形式为1-3(A-C) 4(D)。

我尝试过的

我想到执行的第一步是将会议对象“分组”到具有以下功能的相关子列表中:

def _to_related_lists(list):
    """Given a list of section meeting dicts, return a list of lists, where each sub-list is list of
    related section meetings, either related by period or day"""

    related_list = []
    sub_list = []

    related_values = set()
    for index, section_meeting_object in enumerate(list):
        # starting with empty values list
        if not related_values:
            related_values.add(section_meeting_object['period']['name'])
            related_values.add(section_meeting_object['day']['name'])
            sub_list.append(section_meeting_object)
        elif section_meeting_object['period']['name'] in related_values or section_meeting_object['day']['name'] in related_values:
            related_values.add(section_meeting_object['period']['name'])
            related_values.add(section_meeting_object['day']['name'])
            sub_list.append(section_meeting_object)
        else:
            # no related values found in current section_meeting_object
            related_list.append(sub_list)
            sub_list = []
            related_values = set()
            related_values.add(section_meeting_object['period']['name'])
            related_values.add(section_meeting_object['day']['name'])
            sub_list.append(section_meeting_object)

    related_list.append(sub_list)

    return related_list

返回:

[
    [{
        'period': {
            'sort_order': 1,
            'name': '1'
        },
        'day': {
            'sort_order': 1,
            'name': 'A'
        }
    }, {
        'period': {
            'sort_order': 1,
            'name': '1'
        },
        'day': {
            'sort_order': 2,
            'name': 'B'
        }
    }, {
        'period': {
            'sort_order': 2,
            'name': '2'
        },
        'day': {
            'sort_order': 1,
            'name': 'A'
        }
    }, {
        'period': {
            'sort_order': 2,
            'name': '2'
        },
        'day': {
            'sort_order': 2,
            'name': 'B'
        }
    }],
    [{
        'period': {
            'sort_order': 4,
            'name': '4'
        },
        'day': {
            'sort_order': 3,
            'name': 'C'
        }
    }]
]

如果整个字符串 1-3(A-C) 4(D) 最后是我想要的聚合表达式,让我们调用 1-3(A-C) 和 4(D) “子表达式”。每个相关的子列表都是一个“子表达式”,所以我想我会以某种方式遍历每个子列表并创建子表达式,但我不确定该怎么做。

【问题讨论】:

  • 我一个字都听不懂。
  • 您的输入和输出之间的关系充其量是令人困惑的。例如,如果B 仅对应于2 和3 而不是1-3,那么输出字符串将如何形成?
  • @ason​​gtoruin 输出将是 1(A) 2-3(A-B)
  • sort-order没有意义?

标签: python list dictionary aggregate


【解决方案1】:

首先,让我们将您的列表定义为d_list。

d_list = [
    {'period': {'sort_order': 1, 'name': '1'}, 'day': {'sort_order': 1, 'name': 'A'}},
    {'period': {'sort_order': 1, 'name': '1'}, 'day': {'sort_order': 2, 'name': 'B'}},
    {'period': {'sort_order': 1, 'name': '1'}, 'day': {'sort_order': 1, 'name': 'C'}},
    {'period': {'sort_order': 3, 'name': '3'}, 'day': {'sort_order': 1, 'name': 'A'}},
    {'period': {'sort_order': 3, 'name': '3'}, 'day': {'sort_order': 2, 'name': 'B'}},
    {'period': {'sort_order': 3, 'name': '3'}, 'day': {'sort_order': 2, 'name': 'C'}},
    {'period': {'sort_order': 4, 'name': '4'}, 'day': {'sort_order': 3, 'name': 'D'}},
]

请注意,我使用 python 原生模块string 来定义B 介于A 和C 之间。因此,您可能想要做的是

import string

agg0 = {}
for d in d_list:
    name = d['period']['name']
    if name not in agg0:
        agg0[name] = []
    day = d['day']
    agg0[name].append(day['name'])

agg1 = {}
for k,v in agg0.items():
    pos_in_alph = [string.ascii_lowercase.index(el.lower()) for el in v]
    allowed_indexes = [max(pos_in_alph),min(pos_in_alph)]
    agg1[k] = [el for el in v if string.ascii_lowercase.index(el.lower()) in allowed_indexes]

agg = {}
for k,v in agg1.items():
    w = tuple(v)
    if w not in agg:
        agg[w] = {'ks':[],'gr':len(agg0[k])>2}
    agg[w]['ks'].append(k)
    print agg[w]

str_ = ''
for k,v in sorted(agg.items(), key=lambda item:item[0], reverse=False):
    str_ += ' {pnames}({dnames})'.format(pnames=('-' if v['gr'] else ',').join(sorted(v['ks'])),
                                         dnames='-'.join(k))

print(str_.strip())

输出1-3(A-C) 4(D)


在@NathanJones 的评论之后,请注意如果d_list 被定义为
d_list = [
    {'period': {'sort_order': 1, 'name': '1'}, 'day': {'sort_order': 1, 'name': 'A'}},
    ##{'period': {'sort_order': 1, 'name': '1'}, 'day': {'sort_order': 2, 'name': 'B'}},
    {'period': {'sort_order': 1, 'name': '1'}, 'day': {'sort_order': 1, 'name': 'C'}},
    {'period': {'sort_order': 3, 'name': '3'}, 'day': {'sort_order': 1, 'name': 'A'}},
    {'period': {'sort_order': 3, 'name': '3'}, 'day': {'sort_order': 2, 'name': 'B'}},
    {'period': {'sort_order': 3, 'name': '3'}, 'day': {'sort_order': 2, 'name': 'C'}},
    {'period': {'sort_order': 4, 'name': '4'}, 'day': {'sort_order': 3, 'name': 'D'}},
]

上面的代码将打印1,3(A-C) 4(D)

【讨论】:

  • 每个day 对象中的sort_order 字段表示B 介于A 和C 之间。
  • 当我尝试运行它时,我得到这个错误:AttributeError: module 'string' has no attribute 'lowercase'
  • 感谢您将其更新到 python 3。
  • 差不多。我不认为它在使用sort_order,因为我的输出是3-1(A-C) 4(D)
  • 另外我不认为这个解决方案会考虑相邻的时期/天与非相邻的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-24
  • 1970-01-01
  • 2016-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-06
相关资源
最近更新 更多