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

时间:2013-06-23 14:57:47

标签: python sorting dictionary

如何在特定日期之后获得第一把钥匙?

当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 个答案:

答案 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