【问题标题】:Values between range in javascript arrayjavascript数组中范围之间的值
【发布时间】:2012-08-06 11:57:20
【问题描述】:

我在 java 脚本中有一个按升序排序的数组,其中包含以毫秒为单位的日期。

// Sample data; This may grow upto 1k or 2k
var dates = [1333391400000,1335292200000,1335810600000,1336329000000,1336933800000,1337020200000,
1337193000000,1337538600000,1337625000000,1337797800000,1338316200000,1338921000000,
1339093800000,1339439400000,1340303400000,1341772200000,1342463400000,1343068200000];

我没有开始和结束索引。我有价值观。我需要从 java 脚本数组中获取 2 个日期(最小值和最大值)之间的所有日期。我通过 JSON 从 Java 获取这个数组。

这是获取最小值和最大值之间日期的方法:

function getDatesBetweenRange(min,max){
    var subArray = [];
    var value, jCntr=0;
    for(var iCntr=0;iCntr<dates.length;iCntr++){
         value = dates[iCntr];
         if(value>max)
             break;
         if(value >=min && value <=max){
             subArray[jCntr++]= value;
         }
    }
    return subArray;
}

由于数组是升序排列的;如果我得到的最大值超过了参数中提供的最大值,我将打破循环。

还有其他有效的方法可以从 Java Script 数组中获取值吗?

【问题讨论】:

  • @Roset:我没有开始和结束索引。我有价值观
  • 不是来自数组,但您可以使用更合适的数据结构,例如 range tree
  • @Esailija,然后简单地使用其他最终条件而不是相等。
  • @Esailija 二进制搜索可以识别大于给定值的最小元素和小于给定值的最大元素。你否认吗?..

标签: javascript arrays


【解决方案1】:

这是一个看起来像 more efficient 的半二进制过滤方法(至少在我的浏览器中 - Chrome、Firefox、IE9)

function filterBinary(arr,min,max){
 var len   = arr.length
    ,up    = -1
    ,down  = len
    ,rrange= []
    ,mid   = Math.floor(len/2) 
 ;
 while (up++<mid && down-->mid){
    if (arr[up]>=max || arr[down]<=min){break;}
    if (arr[up]>=min){
      rrange.push(arr[up]);
    }
    if (arr[down]<=max){
      rrange.push(arr[down]);
    }
 }
 return rrange;   
}

【讨论】:

  • 这实际上可能会更有效,但我知道 Array.filter 在某些浏览器中不可用
  • 注意中间值输出两次,例如filterBinary([0,1,2], 0, 2) == [0, 2, 1, 1]。此外,这不是二进制搜索,因此您应该重命名该方法:-)
【解决方案2】:

您可以使用binary search 来获取索引,然后使用slice

Array.prototype.sliceRange = function(min, max) {
    if (min > max) return this.sliceRange(max, min);
    var l = 0,
        r = this.length;
    // find an element at index m that is in range
    rough: {
        while (l < r) {
            var m = Math.floor(l + (r - l) / 2);
            if (this[m] < min)
                l = m + 1;
            else if (this[m] > max)
                r = m;
            else
                break rough;
        }
        // l == r: none was found
        return [];
    }
    var lr = m, // right boundary for left search
        rl = m; // left boundary for right search
    // get first position of items in range (l == lr)
    while (l < lr) {
        m = Math.floor(l + (lr - l) / 2);
        if (this[m] < min)
            l = m + 1;
        else
            lr = m;
    }
    // get last position of items in range (r == rl)
    while (rl < r) {
        m = Math.floor(rl + (r - rl) / 2);
        if (this[m] > max)
            r = m;
        else
            rl = m + 1;
    }
    // return the items in range
    return this.slice(l, r);
}

(Demo)


然而,@Qnan 只进行一次半搜索(而不是我的三次半搜索)的方法更直接,不应该受到任何不利影响。我只会使用导致精确索引的循环:

Array.prototype.sliceRange = function(min, max) {
    if (min > max) return this.sliceRange(max, min);
    var l = 0,
        c = this.length,
        r = c;
    // get first position of items in range (l == c)
    while (l < c) {
        var m = Math.floor(l + (c - l) / 2);
        if (this[m] < min)
            l = m + 1;
        else
            c = m;
    }
    // get last position of items in range (c == r)
    while (c < r) {
        var m = Math.floor(c + (r - c) / 2);
        if (this[m] > max)
            r = m;
        else
            c = m + 1;
    }
    // return the items in range
    return this.slice(l, r);
}

【讨论】:

  • minmax 不一定在数组中,因此它们将返回 -1。二进制搜索也会返回 -1 但更快
  • @Esailija:二进制搜索是指一种二进制算法来返回范围内元素的索引,而不是搜索最小值/最大值本身。仍在处理代码:-)
  • 您仍然不能使用slice,因为您必须线性迭代范围并检查所有这些范围。如果您从Qnan 的答案中删除该线性迭代,它将不起作用。所以不管怎么说,都不能用二分查找来获取索引和切片。
  • 我无法让你的最新编辑工作:-P 无论我输入什么,结果都是空数组。[0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 25, 243, 424].sliceRange(1,3) //[]
  • 哦,我可以 :-) 获得上限和下限的最后一次搜索不需要是线性的。很抱歉对所有编辑造成混淆
【解决方案3】:

尝试像这样优化循环(在达到最大值后打破排序列表)的问题是您每次迭代时都在进行检查。在某些情况下,仅遍历整个列表会更快。特别是当您要搜索的值位于列表末尾时(例如最近的日期)

但是,如果它有所作为,您需要一个二分搜索算法。 Lodash 有一个函数,它使用二进制搜索来检索将插入值的索引。将此与 slice 结合起来,结果应该是最佳的。

使用 [Lodash][1]

// sliced :  get all values between+including min-max from sorted array of numbers
// @param array sorted timestamps
// @param min timestamp minimum
// @param max timestamp maximum
function sliced(array,min,max){
    return array.slice(_.sortedIndex(array, min),_.sortedIndex(array, max)+1);
}

【讨论】:

    【解决方案4】:

    在这种情况下,二分搜索大致如下所示

    var dates = [1333391400000,1335292200000,1335810600000,1336329000000,1336933800000,1337020200000,
    1337193000000,1337538600000,1337625000000,1337797800000,1338316200000,1338921000000,
    1339093800000,1339439400000,1340303400000,1341772200000,1342463400000,1343068200000];
    
    function getDatesBetweenRange(min, max) {
        var subArray = [];
        var value, iCntr;
        var start, end;
    
        var low = 0, high = dates.length - 1;
        while (high - low > 1) {
            centre = Math.floor((high + low) / 2);
            if (dates[centre] < min)
                low = centre;
            else 
                high = centre;
        }
        start = low;
        high = dates.length - 1
        while (high - low > 1) {
            centre = Math.floor((high + low) / 2);
            if (dates[centre] > max)
                high = centre;
            else 
                low = centre;
        }
        end = high;
    
        for (var i = start; i < end; i++) {
            value = dates[i];
            if (value < min) {
                continue;
            }
            if (value > max) {
                break;
            }
            subArray.push(value);
        }
        return subArray;
    }
    
    console.log(getDatesBetweenRange(1337193000000, 1337797800000));​
    

    这遵循@Stano 的代码,除了运行两次二进制搜索以识别更严格的界限。当然可以改进。

    这是小提琴http://jsfiddle.net/EJsmy/1/

    【讨论】:

    • 我明白你的意思是确定一个比给定值更大的值,并承认我没有跳出框框思考,可以简单地线性检查搜索给出的界限清理。我想的更像.slice(bs(min),bs(max)),这当然是做不到的。我要把这个答案浮在bergi的上面。
    • @Esailija 谢谢。实际上,在这里也可以使用 slice(),但问题是要准确识别边界。最简单的方法可能是这样的:jsfiddle.net/3VR4k 如果范围通常很大,这样做可能很有意义
    • 是的,你可以使用 slice,但它不会给出正确的结果(所以,你不能),因为二进制搜索无法识别确切的边界,但我之前错了......哈哈跨度>
    • 是的,必须调整边界,但很容易证明调整不会超过 O(1) 时间,所以总体 O(log(n))被保留:jsfiddle.net/3VR4k/4;附言抱歉,我之前的评论包含指向错误小提琴的链接。
    • 我认为,您可以使用 dates.slice(start+1, end); 获得正确的结果 - low 是范围之前的项目,high 是之后的项目
    【解决方案5】:
      function getDatesBetweenRange(min,max)
        {   
          var subArray = dates.slice(0,dates.length-1);
    
          for(var iCntr=0;iCntr<dates.length;iCntr++)
           {
           if(!(dates[iCntr] >=min && dates[iCntr] <=max))
              {
                 subArray.splice(iCntr,1); 
              }
            }
           return subArray; 
       } 
    

    【讨论】:

    • 这也会改变原来的dates数组。
    • 这行不通。它将删除 iCntr 位置的 1 个元素
    • @OlegV.Volkov:- 现在还好吗??
    • @AshwinPrabhu:尝试过尝试编辑器及其工作(w3school)
    • @Anonymous,不。你正在做浅拷贝,根据问题的定义,原始数组中可能有很多元素。这会大大降低性能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2017-10-14
    • 2013-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-18
    相关资源
    最近更新 更多