如何在特定日期之后获得第一把钥匙?
当date_table越来越大时,最佳解决方案是什么?
def get_first():
date_table = {
'this is example 1': '01:20 2013-08-07',
'this is example 2': '11:45 2012-03-23',
'this is example 3': '19:00 2013-12-01',
}
certain_date = '12:14 2013-06-23'
# TODO: sort, filter
print get_first()
>> 'this is example 1'
答案 0 :(得分:4)
您必须排序然后过滤,并解析结构中的所有日期:
from datetime import datetime
certain_date = datetime.strptime(certain_date, '%H:%M %Y-%m-%d')
match = next((k for v, k in sorted((datetime.strptime(v, '%H:%M %Y-%m-%d'), k) for k, v in date_table.iteritems()) if v >= certain_date), None)
演示:
>>> certain_date = datetime.strptime(certain_date, '%H:%M %Y-%m-%d')
>>> next((k for v, k in sorted((datetime.strptime(v, '%H:%M %Y-%m-%d'), k) for k, v in date_table.iteritems()) if v >= certain_date), None)
'this is example 1'
另一种方法是过滤所有日期并且最接近搜索日期的日期:
from datetime import datetime, timedelta
parse = lambda d: datetime.strptime(d, '%H:%M %Y-%m-%d')
certain_date = parse(certain_date)
match = min(date_table, key=lambda k: parse(date_table[k]) - certain_date if parse(date_table[k]) > certain_date else timedelta.max)
演示:
>>> min(date_table, key=lambda k: parse(date_table[k]) - certain_date if parse(date_table[k]) > certain_date else timedelta.max)
'this is example 1'
您真的想重新考虑您的结构,并使用像堆队列或btree这样的东西来保持您的数据结构更易于访问这种访问。
即使是带有解析后的(datetime, key)
元组的排序列表也会更好地执行更多,因为bisect
module可以让您在O(log n)时间内找到“下一个”值而不是用于排序的O(n log n)或用于复杂min()
过滤器的O(n)。
您可以使用以下命令快速将结构转换为以下列表:
from functools import total_ordering
@total_ordering
class Entry(object):
def __init__(dt, key):
self.dt = dt
self.key = key
def __eq__(self, other):
if not isinstance(other, type(self)): return NotImplemented
return self.dt == other.dt and self.key == other.key
def __lt__(self, other):
if not isinstance(other, type(self)): return NotImplemented
if self.dt < other.dt:
return True
return self.dt == other.dt and self.key < other.key
date_list = [Entry(datetime.strptime(v, '%H:%M %Y-%m-%d'), k) for v, k in date_table.iteritems()]
date_list.sort()
然后找到你的下一场比赛:
import bisect
match = date_list[bisect.bisect(date_list, Entry(current_date, None))]
并使用bisect.insort()
对列表进行排序。
答案 1 :(得分:1)
当date_table越来越大时,最佳解决方案是什么?
字典不适合范围检索(即当您想要根据其键检索一系列值时)。这是因为字典使用散列来存储密钥,因此不能保证排序顺序,但是为了这种权衡,它们确实为任何特定密钥提供恒定的时间查找。
对于范围检索,最好的办法是使用某种形式的平衡二叉搜索树,我相信如果你谷歌的话,有很多Python的实现。这些允许您在对数时间执行范围检索,这显然比常数慢,但肯定比线性更快。
话虽这么说,如果你完全知道你的字典不会超过某个小尺寸,那么在按键上使用线性搜索是完全可以接受的,因为性能上的差异可以忽略不计。
答案 2 :(得分:0)
您可以在此使用datetime
模块和min
:
>>> from datetime import datetime, timedelta
>>> certain_date = '12:14 2013-06-23'
>>> c_d = datetime.strptime(certain_date, "%H:%M %Y-%m-%d")
>>> def func(x):
d = datetime.strptime(x[1], "%H:%M %Y-%m-%d")
delta = d - c_d if d > c_d else timedelta.max
return delta
...
>>> min(date_table.items(), key = func)
('this is example 1', '01:20 2013-08-07')
>>> min(date_table.items(), key = func)[0]
'this is example 1'
datetime.strptime
将日期转换为日期时间对象,因此c_d
现在看起来像这样:
>>> c_d
datetime.datetime(2013, 6, 23, 12, 14)
现在在func
内:
delta = d - c_d if d > c_d else timedelta.max
检查当前项目的日期是否比c_d
更新,如果是,则返回其差异,否则返回timedelta.max
。
timedelta.max
的位置:
>>> timedelta.max
datetime.timedelta(999999999, 86399, 999999)
答案 3 :(得分:0)
您甚至可以在不将字符串转换为datetime
个对象的情况下离开,这是使用bisect
的选项:
from operator import itemgetter
from bisect import bisect
name, tds = zip(*sorted(( (k, v.split()[::-1]) for k, v in date_table.iteritems()), key=itemgetter(1)))
certain_date = '12:14 2013-06-23'.split()[::-1]
print name[bisect(tds, certain_date)]
# this is example 1