如果你的列表很长,预处理l2
可以值得使用bisect
来查找最接近的日期。然后,在l1
中查找最接近的日期将是O(log(len(l2))),而不是使用min
的O(len(l2))。
from datetime import datetime
from bisect import bisect
l1 = [ '09/12/2017', '10/24/2017' ]
l2 = [ '09/15/2017', '10/26/2017', '12/22/2017' ]
dates = sorted(map(lambda d: datetime.strptime(d, '%m/%d/%Y'), l2))
middle_dates = [dates[i] + (dates[i+1]-dates[i])/2 for i in range(len(dates)-1)]
out = [l2[bisect(middle_dates, datetime.strptime(d,'%m/%d/%Y'))] for d in l1]
print(out)
为了回应你的最后一条评论,这里有另一个解决方案,使用迭代器和生成器,遍历
l1
和
l2
开头的必要部分。
from datetime import datetime
from itertools import tee, islice, zip_longest
def closest_dates(l1, l2):
"""
For each date in l1, finds the closest date in l2,
assuming the lists are already sorted.
"""
dates1 = (datetime.strptime(d, '%m/%d/%Y') for d in l1)
dates2 = (datetime.strptime(d, '%m/%d/%Y') for d in l2)
dinf, dsup = tee(dates2)
enum_middles = enumerate(d1 + (d2-d1)/2
for d1, d2 in zip_longest(dinf, islice(dsup, 1, None),
fillvalue=datetime.max))
out = []
index, middle = next(enum_middles)
for d in dates1:
while d > middle:
index, middle = next(enum_middles)
out.append(l2[index])
return out
一些测试:
l1 = [ '09/12/2017', '10/24/2017', '12/11/2017', '01/04/2018' ]
l2 = [ '09/15/2017', '10/26/2017', '12/22/2017' ]
print(closest_dates(l1, l2))
l2 = ['11/11/2018']
print(closest_dates(l1, l2))
l1 = ['09/16/2018', '01/01/2018']
和l2 = [ '09/15/2017', '12/24/2017']
,你的第二个解决方案输出了['12/24/2017', '09/15/2017']
。第一个解决方案是错误的,因为插入点在第一个小于目标日期的日期之后,而不是最接近的日期。第二个解决方案失败是因为在函数中进行了字典序比较,而不是日期比较。此外,由于每次调用函数时都从index=0
开始,所以复杂度仍为 0(len(l1) * len(l2))。 - Thierry Lathuille