如何保持Javascript数组排序,而不对其进行排序

Posted

技术标签:

【中文标题】如何保持Javascript数组排序,而不对其进行排序【英文标题】:How to keep Javascript array sorted, without sorting it 【发布时间】: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中。

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

【问题讨论】:

【参考方案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

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

【讨论】:

线性搜索无法随元素数量很好地扩展。

以上是关于如何保持Javascript数组排序,而不对其进行排序的主要内容,如果未能解决你的问题,请参考以下文章

查找未排序列表的第 N 项而不对列表进行排序

如何对多个 mysql foreach 数组进行排序并需要它们在 php 中保持一致?

使用javascript对数组进行排序而不改变空或空的地方

如何在定义变量后不对其进行评估?

如何从 buildscript 发送 Artifactory 发布凭据而不对其进行硬编码?

数组排序中的 JavaScript 与 PHP