Python 如何对字典中的日期字符串进行排序

Python 如何对字典中的日期字符串进行排序,python,sorting,dictionary,Python,Sorting,Dictionary,如何在特定日期后获取第一个密钥 当日期表变大时,最好的解决方案是什么 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

如何在特定日期后获取第一个密钥

当日期表变大时,最好的解决方案是什么

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'

您必须先进行排序,然后进行筛选,并解析结构中的所有日期:

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)
演示:

另一种方法是过滤所有在搜索日期之后且最接近搜索日期的日期:

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)
演示:

您确实希望重新考虑您的结构,并使用堆队列或btree之类的东西来保持数据结构更易于访问

即使是带有已解析的
(datetime,key)
元组的已排序列表也会表现得更好,因为可以让您在O(logn)时间内找到“下一个”值,而不是在O(n logn)时间内进行排序,或者在复杂的
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()

您可以使用
bisect.insort()
对列表进行排序。

您可以在此处使用
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.strtime
将日期转换为datetime对象,因此
c\u d
现在看起来像这样:

>>> c_d
datetime.datetime(2013, 6, 23, 12, 14)
现在在
func
中:

delta =  d - c_d if d > c_d else timedelta.max
这将检查当前项目的日期是否比
c\u d
新,如果是,则返回它们的差异,否则返回
timedelta.max

其中
timedelta.max
为:

>>> timedelta.max
datetime.timedelta(999999999, 86399, 999999)
当日期表变大时,最好的解决方案是什么

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'
字典不太适合范围检索(也就是说,当您希望根据其键检索一个值范围时)。这是因为字典使用散列来存储键,因此无法保证排序顺序,但是为了实现这种折衷,它们确实为任何特定键提供了恒定的时间查找

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


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

您甚至可以不将字符串转换为
datetime
对象而逃之夭夭,这里有一个使用对分的选项:

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

作为一个.net的家伙,我四处看看,我很惊讶python没有内置的bst…除非我遗漏了什么,顺便说一句,只是个人观察。与你的答案无关:)我发现二进制搜索树总是需要额外的调整,这在库中很难捕获。二元搜索树本身很简单,但它们并不好,它们需要平衡,这可能有点痛苦。
>>> timedelta.max
datetime.timedelta(999999999, 86399, 999999)
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