搜索按整数时间戳排序的列表的简单方法

时间:2012-09-13 11:16:45

标签: python search

我有一个表单的日志记录条目列表:

[{'time': 199920331000, 'message': 'message1'}, {'time': 199920331001, 'message': 'message2'}...]

其中时间值总是通过列表增加。如果我想在给定时间戳之后获取日志,我可以遍历元素,直到看到大于给定时间戳的时间戳:

def getLog(timestamp):
    global logs
    for x in range(len(logs)):
        if logs[x]['time'] > timestamp:
            return logs[x:]
    return []

我想在python 3中已经有了一个快速搜索机制,但不知道在哪里看。

3 个答案:

答案 0 :(得分:4)

如果我理解正确,您正在寻找bisect module,它实现了一种有效的算法,用于查找排序列表中的值大于或小于给定值的点。

您的日志条目必须是实现某种形式的排序的类。像这样:

from functools import total_ordering

@total_ordering
class LogEntry(object):
    def __init__(self, time, message):
        self.time = time
        self.message = message

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return self.time == other.time and self.message == other.message

    def __lt__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        if self.time == other.time:
            return self.message < other.message
        return self.time < other.time

这些LogEntry类是可订购的(在functools.total_ordering class decorator的帮助下),因此bisect模块知道哪些条目的值低于其他值。

您的功能将变为:

def getLog(timestamp):
    dummy_entry = LogEntry(timestamp, '')
    index = bisect.bisect_right(logs, dummy_entry)
    return logs[index:]

请注意,我们无需声明logs全局,因为您没有分配给它。

答案 1 :(得分:2)

鉴于Python未在b.__gt__(a)未实现时尝试a.__lt__(b),您不需要更改日志条目的类,它应足以提供足够智能的密钥:

import bisect
from functools import total_ordering
from operator import itemgetter

log = [
    {'time': 199920331000, 'message': 'message1'},
    {'time': 199920331001, 'message': 'message2'},
    # ...
]

@total_ordering
class Key(object):
    def __init__(self, keyfunc, keyval):
        self.keyfunc = keyfunc
        self.keyval = keyval

    def __eq__(self, other):
        return self.keyval == self.keyfunc(other)

    def __lt__(self, other):
        return self.keyval < self.keyfunc(other)

start = bisect.bisect(log, Key(itemgetter("time"), 199920331000))
print log[start:]

或者,您可以在视图列表中包装视图:

def keyed(items, key):
    class View(object):
        def __getitem__(self, index):
            return key(items[index])
        def __len__(self):
            return len(items)
    return View()

start = bisect.bisect(keyed(log, itemgetter("time")), 199920331000)
print log[start:]

(这是从Smart way to delete tuples

中删除的。)

答案 2 :(得分:1)

如果您知道时间总是增加,则可以保证您的列表已排序。 然后我会使用here的答案并尝试对其进行调整,如下所示:

def binary_search(log_list, timestamp, lo=0, hi=None):
    if hi is None:
        hi = len(log_list)
    while lo < hi:
        mid = (lo+hi)//2
        midval = log_list[mid]['time']
        if midval < timestamp:
            lo = mid+1
        elif midval > timestamp: 
            hi = mid
        else:
            return mid
    return -1

(尚未测试)