在序列javascript数组中查找范围的最有效方法

时间:2013-03-04 16:55:29

标签: javascript

我有一系列连续的时间戳。我需要抓住介于开始和结束时间之间的数组子集。简化示例如下所示:

timestamps = [ 5,7,12,13,15,23 ];
startTime  = 6;
endTime    = 18;

鉴于上面的例子,找到介于startTimeendTime之间的第一个和最后一个时间戳的索引的最有效方法是什么?

正确的脚本会发现并返回索引1& 4(timestamps[1], timestamps[4]

我可以遍历数组并进行比较,但是有更有效的方法吗?


编辑::我的解决方案 - 二进制搜索:

(适用的CoffeeScript)

 # Search ordered array for timestamps falling between 'start' & 'end'
 getRangeBorderIndexes = (stack, start, end) ->
  ar    = []
  ar[0] = getBorder( stack, start, "left" )
  ar[1] = getBorder( stack, end, "right"  )
  return ar

# Use bisection (binary search) to find leftmost or rightmost border indexes
getBorder = (stack, value, side ) ->
  mod1       = if side == "left"  then 0 else -1 
  mod2       = if side == "left"  then 1 else  0

  startIndex = 0
  stopIndex  = stack.length - 1
  middle     = Math.floor( (stopIndex+startIndex)/2 )

  while stack[middle] != value && startIndex < stopIndex
    if value < stack[middle]
      if value > stack[middle - 1] then return middle + mod1
      stopIndex = middle - 1

    else if value > stack[middle]
      if value < stack[middle + 1] then return middle + mod2
      startIndex = middle + 1

    middle = Math.floor( (stopIndex+startIndex)/2 )

  return if stack[middle] != value then -1 else middle

timestamps = [ 5,7,12,13,15,23 ]
startTime  = 6
endTime    = 18

getRangeBorderIndexes( timestamps, startTime, endTime) # returns [1,5]

@kennebec和@Shanimal给出了很好的回应,特别是如果你想要一个超级简单的方法来获取一个数组的子集。但是我需要子数组的索引而不是整个子数组。我做了一些测试,上面的例子一直需要大约7ms才能找到子数组的边界,即使在一个有1000万条目的数组上也是如此!

感谢@voithos指出我正确的方向。我还修改了this code以创建上面的解决方案。

2 个答案:

答案 0 :(得分:2)

使用lodash / underscore库:

_.select(timestamps,function(i){
    return i>=startTime && i<=endTime;
})

答案 1 :(得分:1)

循环很有效,但Array.filter更容易 -

var timestamps = [ 5,7,12,13,15,23 ],

startTime  = 6,
endTime    = 18;


var selected=timestamps.filter(function(itm){
return itm>=startTime   && itm<=endTime;
});

/ *返回值:(数组) 7,12,13,15 * /

//过滤器内置于大多数浏览器中,但如果您需要在#9之前支持IE,则可以使用它:

if(!Array.prototype.filter){
    Array.prototype.filter= function(fun, scope){
        var T= this, A= [], i= 0, itm, L= T.length;
        if(typeof fun== 'function'){
            while(i<L){
                if(i in T){
                    itm= T[i];
                    if(fun.call(scope, itm, i, T)) A[A.length]= itm;
                }
                ++i;
            }
        }
        return A;
    }
}