递归列表函数

3

我正在尝试创建一个递归的Python函数,它接受一个时间段列表,并将它们合并成一个干净的时间线。它应该扫描列表并应用以下规则:

  • 如果在时间段中找到 None 的值: 用datetime.date.today()替换None

  • 如果一个时间段在另一个时间段内开始在另一个时间段内结束删除它.

  • 如果一个时间段在另一个时间段之前开始在另一个时间段内结束延长开始日期

  • 如果一个时间段在另一个时间段内开始在另一个时间段之后结束延长结束日期

  • 如果一个时间段在另一个时间段之后开始在另一个时间段之后结束保留它,这是一个独立的时间段

  • 如果一个时间段在另一个时间段之前开始在另一个时间段之前结束保留它,这是一个独立的时间段

可能更容易通过示例说明输入和期望的输出(假设值已用datetime格式化):

[I] = [(01/2011, 02/2015), (04/2012, 08/2014), (09/2014, 03/2015), (05/2015, 06/2016)]
[O] = [(01/2011, 03/2015), (05/2015, 06/2016)]  
# Notice how the output has produced a set of minimum length whilst covering all periods.

[I] = [(07/2011, 02/2015), (04/2012, 08/2014), (09/2014, 04/2015), (06/2015, None)]
[O] = [(07/2011, 04/2015), (06/2015, date.today())]
# Also, notice how the output has amended None, so it can compare dates.

感谢 @khredos 的帮助,但我仍然无法输出所需的最小字符串:
from datetime import datetime

# Here is an example list of time periods
periods = [('01/2011', '02/2015'), ('04/2012', '08/2014'), ('09/2014', '03/2015'), ('05/2015', '06/2016')]

# this lambda function converts a string of the format you have specified to a 
# datetime object. If the string is None or empty, it uses today's date
cvt = lambda ds: datetime.strptime(ds, '%m/%Y') if ds else datetime.today()

# Now convert your original list to an iterator that contains datetime objects
periods = list(map(lambda s_e : (cvt(s_e[0]), cvt(s_e[1])), periods))

# Next get the start dates into one list and the end dates into another
starts, ends = zip(*periods)

# Finally get the timeline by sorting the two lists
timeline = sorted(starts + ends)

# Output: [datetime.datetime(2011, 1, 1, 0, 0), datetime.datetime(2012, 4, 1, 0, 0), datetime.datetime(2014, 8, 1, 0, 0), datetime.datetime(2014, 9, 1, 0, 0), datetime.datetime(2015, 2, 1, 0, 0), datetime.datetime(2015, 3, 1, 0, 0), datetime.datetime(2015, 5, 1, 0, 0), datetime.datetime(2016, 6, 1, 0, 0)]

请同时发布您遇到的“错误”。 - R Nar
感谢反馈。请查看问题底部添加的错误信息。我之所以出现错误是因为试图篡改一对中的一半。 - alpacinohead
元组是不可变的:您无法更改其值。如果要更改它,请创建一个新元组或使用列表。 - Prune
1个回答

2
from datetime import datetime

# Here is an example list of time periods
periods = [('01/2011', '02/2015'), ('04/2012', '08/2014'), ('09/2014', '03/2015'), ('05/2015', '06/2016')]

# this lambda function converts a string of the format you have specified to a 
# datetime object. If the string is None or empty, it uses today's date
cvt = lambda ds: datetime.strptime(ds, '%m/%Y') if ds else datetime.today()

可用的格式在这里
# Now convert your original list to an iterator that contains datetime objects
periods = list(map(lambda s_e : (cvt(s_e[0]), cvt(s_e[1])), periods))

# Next get the start dates into one list and the end dates into another
starts, ends = zip(*periods)

# Finally get the timeline by sorting the two lists
timeline = sorted(starts + ends)

输出应该类似于:
[datetime.datetime(2011, 1, 1, 0, 0), datetime.datetime(2012, 4, 1, 0, 0), datetime.datetime(2014, 8, 1, 0, 0), datetime.datetime(2014, 9, 1, 0, 0), datetime.datetime(2015, 2, 1, 0, 0), datetime.datetime(2015, 3, 1, 0, 0), datetime.datetime(2015, 5, 1, 0, 0), datetime.datetime(2016, 6, 1, 0, 0)]

尝试使用您拥有的任何日期列表,您应该观察到相同的行为。

希望对您有所帮助。


太棒了!感谢您的快速回复并设法保持代码简洁。由于我无法在Python 3中解包元组,因此出现错误。这一行出现问题:“periods = map(lambda (s, e) : (cvt(s), cvt(e)), periods)”。另外,输出不是列表,因此我不知道哪些日期被分组在一起。您是否可以更新您的答案,以便输出一个与输入列表相同的带有开始和结束日期的列表? - alpacinohead
@AaronAvocado,刚刚更新了答案,使用了Python 3的语法。不幸的是,看起来Python 3不支持元组参数解包,这就是为什么出现错误的原因。此外,map类返回一个迭代器,这就是为什么输出不是列表的原因。通过此次编辑,所有问题都已经解决了。 - smac89
非常好,谢谢您的更新@khredos。唯一的问题是输出数组没有创建一个“干净”的时间线。日期(“04/2015”,“08/2014”)不应该出现在输出中,因为它们落在期间(“01/2011”,“02/2015”)内。代码的目标是删除在另一个周期内开始和结束的周期;扩展在另一个周期内开始但在外部结束或在另一个周期内结束但在外部开始的周期;在另一个周期之外开始和结束的周期应显示为单独的组。 - alpacinohead

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