【问题标题】:How to keep Javascript array sorted, without sorting it如何保持Javascript数组排序,而不对其进行排序
【发布时间】:2013-12-19 13:23:19
【问题描述】:

我有一个 Node.js 应用程序,我必须经常做以下事情: - 检查特定数组是否已经包含某些元素 - 如果元素确实存在,更新它 - 如果元素不存在,则将其推送到数组中,然后使用下划线 _.sortBy 对其进行排序

为了检查元素是否已经存在于数组中,我使用了这个二分查找函数: http://oli.me.uk/2013/06/08/searching-javascript-arrays-with-a-binary-search/

这样,当数组的大小变大时,排序就会越来越慢。 我假设数组大小可能会增长到每个用户最多 20 000 个项目。最终会有成千上万的用户。该数组按一个键排序,这是一个相当短的字符串。如果需要,可以将其转换为整数。

所以,我需要一种更好的方法来保持数组排序, 而不是每次将新元素推送到其上时对其进行排序。

所以,我的问题是,我应该/如何编辑我使用的二进制搜索算法,以使我能够 如果数组中尚不存在新元素,则获取应放置新元素的数组索引? 或者还有什么其他的可能性来实现这一点。当然,我可以使用某种循环,从头开始遍历数组,直到找到新元素的位置。

所有数据都存储在MongoDB中。

换句话说,我希望在每次推送新元素时保持数组排序而不对其进行排序。

【问题讨论】:

    标签: javascript arrays node.js sorting


    【解决方案1】:

    很容易修改这个binaryIndexOf 函数以在没有找到匹配项时返回下一个元素的索引:

    function binaryFind(searchElement) {
      'use strict';
    
      var minIndex = 0;
      var maxIndex = this.length - 1;
      var currentIndex;
      var currentElement;
    
      while (minIndex <= maxIndex) {
        currentIndex = (minIndex + maxIndex) / 2 | 0; // Binary hack. Faster than Math.floor
        currentElement = this[currentIndex];
    
        if (currentElement < searchElement) {
          minIndex = currentIndex + 1;
        }
        else if (currentElement > searchElement) {
          maxIndex = currentIndex - 1;
        }
        else {
          return { // Modification
            found: true,
            index: currentIndex
          };
        }
      }      
    
      return { // Modification
        found: false,
        index: currentElement < searchElement ? currentIndex + 1 : currentIndex
      };
    }
    

    所以,现在它返回如下对象:

    {found: false, index: 4}
    

    其中index 是找到的元素或下一个元素的索引。

    所以,现在插入一个新元素将如下所示:

    var res = binaryFind.call(arr, element);
    if (!res.found) arr.splice(res.index, 0, element);
    

    现在您可以将binaryFind 添加到Array.prototype 以及一些用于添加新元素的助手:

    Array.prototype.binaryFind = binaryFind;
    
    Array.prototype.addSorted = function(element) {
      var res = this.binaryFind(element);
      if (!res.found) this.splice(res.index, 0, element);
    }
    

    【讨论】:

    • 太棒了!非常感谢你((:这就是我所需要的!
    • @Juha-MattiHurnasti 我编辑了我的答案几次,现在它是正确的。
    • 我发现这很有用,但我相信它无法处理数组存在空值的情况。它只是认为它始终与搜索元素匹配。将第一个 if 条件更改为 if (currentElement === null || currentElement &lt; searchElement) { 并将“未找到”返回索引更改为 index: (currentElement &lt; searchElement || currentElement === null) ? currentIndex + 1 : currentIndex 似乎在我的简短测试中有效,并且具有比任何其他值更少处理空值的效果。
    • 注意使用| 0. 它比 Math.floor() 快 - 见measurethat.net/Benchmarks/Show/953/0/…
    【解决方案2】:

    如果您的数组已经排序并且您想要插入一个元素,为了保持它的排序,您需要将它插入到数组中的特定位置。幸运的是,数组有一个方法可以做到这一点: Array.prototype.splice

    所以,一旦你得到你需要插入的索引(你应该通过对你的二分搜索进行简单的修改来获得),你可以这样做:

    myArr.splice(myIndex,0,myObj);
    // myArr your sorted array
    // myIndex the index of the first item larger than the one you want to insert
    // myObj the item you want to insert
    

    编辑:您的二进制搜索代码的作者也有同样的想法:

    因此,如果您想插入一个值并想知道应该在哪里插入 把它,你可以运行函数并使用返回的数字 将值拼接到数组中。 Source

    【讨论】:

    • 问题是找到添加它的索引。这就是我想问的问题。
    【解决方案3】:

    我知道这是一个老问题的答案,但是下面使用 javascripts array.splice() 非常简单。

    function inOrder(arr, item) {
        /* Insert item into arr keeping low to high order */
    
        let ix = 0;
        while (ix < arr.length) {
            //console.log('ix',ix);
            if (item < arr[ix]) { break; }
            ix++;
        }
    
        //console.log('  insert:', item, 'at:',ix);
        arr.splice(ix,0,item);
        return arr
    }
    

    通过反转测试可以将顺序从高到低更改

    【讨论】:

    • 线性搜索无法随元素数量很好地扩展。
    猜你喜欢
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2022-01-10
    • 2018-06-06
    相关资源
    最近更新 更多