【问题标题】:How to structure time specific data so that the most recent point can be found?如何构造特定时间的数据以便可以找到最近的点?
【发布时间】:2013-01-17 00:05:41
【问题描述】:

这有点难以用单行问题来表达,但我正在寻找一些建议/最佳实践来构建数据和用 Javascript 编写函数。

我有几个项目会定期更改状态。我的数据包含 itemID、时间戳和状态。我目前将它构建为一个对象数组(对于每个项目),具有包含时间戳和状态的历史优先级。 (见下文)。

我正在寻找一种功能,它可以让我使用最近的过去更新轻松获取每个对象在给定时间的状态。我不确定我的数据结构是否允许这样做,或者如果允许,如何编写函数。 (在本例中,我将把时间戳缩短为 4 位数字)

 var items = [
      { id: 1,
        history: {1234: 'open', 1256: 'in-use', 1289: 'reset', 1293: 'open'},
      { id: 2,
        history: {1230: 'open', 1290: 'in-use'},
      { id: 3,
        history: {1238: 'open', 1241: 'in-use', 1251: 'reset'}
 ]

我希望能够拥有这样的功能:

 getStatus(1260);

然后回来

 {1: 'in-use', 2: 'open', 3: 'reset'}

每个id,根据查询时间之前的最近历史记录传入的时间的状态。

我完全不依赖这个数据结构。我还尝试让历史记录包含时间和状态的对象数组,但这意味着我每次都必须遍历整个数组。我最大的问题是我的头脑正在推动我使用 SQL 方法来执行此操作,但我被困在客户端 Javascript...

我的问题:最好的数据结构是什么?以及我将如何编写我的 getStatus() 函数?

谢谢!

【问题讨论】:

    标签: javascript function data-structures


    【解决方案1】:

    我还尝试让历史记录包含时间和状态的对象数组,但这意味着我每次都必须遍历整个数组。

    如果您对数组进行了排序,则不会,因为您可以直接访问最近的日期。您还可以使用二进制搜索来获取特定时间戳的状态。对于您当前拥有的对象,您始终必须枚举所有属性以找到最佳匹配。

    var items = [
      { id: 1,
        history: [
          { timestamp: 1234, status: 'open'},
          { timestamp: 1256, status: 'in-use'},
          { timestamp: 1289, status: 'reset'},
          { timestamp: 1293, status: 'open'}
        ]
      },
      …
    ];
    function index(arr, compare) { // binary search, with custom compare function
        var l = 0,
            r = arr.length - 1;
        while (l <= r) {
            var m = l + ((r - l) >> 1);
            var comp = compare(arr[m]);
            if (comp < 0) // arr[m] comes before the element
                l = m + 1;
            else if (comp > 0) // arr[m] comes after the element
                r = m - 1;
            else // this[m] equals the element
                return m;
        }
        return l-1; // return the index of the next left item
                    // usually you would just return -1 in case nothing is found
    }
    // example:
    function insertItem(history, timestamp, status) {
        var i = index(history, function(item) {
            return item.timestamp - timestamp;
        });
        history.splice(i+1, 0, {timestamp: timestamp, status: status});
    }
    
    function getStatus(time) {
        var result = {};
        function comparefn (item) {
            return item.timestamp - time;
        }
        for (var i=0; i<items.length; i++) {
            var pos = index(items[i].history, comparefn);
            result[items[i].id] = pos == -1
              ? undefined
              : items[i].history[pos].status;
        }
        return result;
    }
    

    【讨论】:

    • 哇.. 好的.. 所以我肯定会切换到阵列。现在我需要花一些时间来理解这段代码.....
    • 好的..我对索引函数有些困惑-它看起来像二进制排序,但我不确定按位运算在做什么...特别是 var m = l + (( r - l) >> 1);这会除以 2 并取整吗?
    • 是的。它是Math.floor( (r-l) / 2) 的缩写,使用bitwise right-shift operator。而且它不是二进制排序,而是binary search :-)
    • 好的。我想我明白了。非常感谢!
    • Bergi,我知道我迟到了 2 周,但我希望你能指出我正确的方向.. 与原始帖子相同的情况,但现在我们需要添加搜索给定时间之前最近的“使用中”。现在我将一个过滤后的数组传递给你给我的代码,但这本质上是 2 次搜索而不是 1 次。您对如何更有效地做到这一点有什么建议吗?
    【解决方案2】:

    你可以使用循环:

    // for each entry:
    var n2 = n;
    while (typeof items[n2] == "undefined" && n2 >= -1) {
        n2--;
    }
    if (n != -1) {returnedArray[idBeingProcessed] = items[n2];}
    else {alert("Error handling here");}
    // repeat for each item (id 1, id 2...)
    

    如果找到答案,这将停止。它可能效率低下,但嘿,它有效:-)

    此外,如果适用,请考虑为您的 history 对象使用数组。

    【讨论】:

    • 你真的推荐循环所有可用的时间戳吗?
    • 正如我所说,它效率低下,但我能想到的唯一解决方案。此外,您只循环遍历entry &lt; n &lt; query 的时间戳,而不是整个对象/数组/任何东西。再说一次,不是最好的,但它可以工作,而且还不如从 A-Z 循环数组那么糟糕。
    • 取决于数组/对象的稀疏程度。以毫秒为单位考虑时间戳:-) 尽管如此,即使对于 OP 提供的示例数据,我也希望 for-in 循环更快。如果没有找到,您的解决方案不会捕获,它会循环直到-Infinity...
    • @Bergi 修复了-Infinity 错误;不错的收获。我确实检查了你的东西;有趣的是,循环整个数组会比我的解决方案更好。会考虑;谢谢!
    猜你喜欢
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 2021-11-27
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多