3

如何在某个日期后获得第一个密钥?

当 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'
4

4 回答 4

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模块可以让您在 O(log n) 时间内找到“下一个”值,而不是 O(n log n) 用于排序或 O(n) 用于复杂min()筛选。

您可以通过以下方式快速将您的结构变成这样的列表:

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()用来保持列表排序。

于 2013-06-23T15:01:45.400 回答
1

当 date_table 变大时,最好的解决方案是什么?

字典不太适合范围检索(即当您想要根据键检索一系列值时)。这是因为字典使用散列来存储键,因此不能保证排序顺序,但是为了这种权衡,它们确实为任何特定键提供了恒定的时间查找。

对于范围检索,你最好的选择是使用某种形式的平衡二叉搜索树,如果你用谷歌搜索,我相信 Python 有很多实现。这些允许您在对数时间执行范围检索,这显然比常数慢,但绝对比线性快。

话虽如此,如果您绝对知道您的字典不会超过某个小尺寸,那么对键使用线性搜索是完全可以接受的,因为性能差异可以忽略不计。

于 2013-06-23T15:08:46.833 回答
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)
于 2013-06-23T15:03:53.123 回答
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
于 2013-06-23T16:37:22.477 回答