Javascript/NodeJS:通过对象数组/集合中的id查找特定对象的最佳方法

Javascript/NodeJS: Best way to find an specific object by id inside an array/collection of objects

本文关键字:对象 id 查找 方法 最佳 集合 NodeJS Javascript 数组      更新时间:2023-09-26

概述

所以我从数据库中提取了一个文档。里面是一个嵌套的对象集合。此嵌套集合中的每个对象都有一个"_id"属性。我想使用Javascript在这些对象上找到一个特定的"_id">


示例

http://jsfiddle.net/WilsonPage/tNngT/


替代示例

http://jsfiddle.net/WilsonPage/tNngT/3/


问题

  1. 我的例子是实现这一目标的最佳方式吗
  2. 这个会在Node.js中阻塞吗

是的,如果您只知道一个对象(保存在数组中(所包含的特定值,则需要在整个结构上循环并比较这些值。

正如您所做的一样,当您找到迭代(示例中的return(时,中断迭代
因此,我对您的第一个问题的回答是,就性能而言,这是正确和最好的方法。

我没有得到的是">Async"示例。您只是移动了代码并更改了结构。您的代码仍然处于"阻塞"状态,因为您正在使用普通的for-loop进行搜索。如果该数组巨大,它将在循环完成所需的时间内阻止您的节点应用程序。

要真正实现异步,您需要去掉任何loop。你需要用跑道计时器在结构上盘旋。

var findById = function(collection, _id, cb){
    var coll = collection.slice( 0 ); // create a clone
    (function _loop( data ) {
        if( data._id === _id ) {
            cb.apply( null, [ data ] );
        }
        else if( coll.length ) {
            setTimeout( _loop.bind( null, coll.shift() ), 25 );
        }
    }( coll.shift() ));
};

然后像一样使用

findById( myCollection, 102, function( data ) {
    console.log('MATCH -> ', data);
});

通过这种技术(这是一个简化的示例(,我们正在创建一个自调用匿名函数,并传入第一个数组项(使用.shift()(。我们进行比较,如果找到了要查找的项,则执行调用方需要提供的回调函数。如果我们没有匹配,但数组仍然包含元素(检查.length(,我们使用setTimeout创建25ms的超时,并再次调用_loop函数,这次是使用下一个数组项,因为.shift()获取并删除第一个条目。我们重复这一步骤,直到没有留下任何项目或找到元素为止。由于setTimeout为JS主线程(在浏览器上,UI线程(中的其他任务提供了做事的机会,因此我们不会阻止和破坏整个节目。

正如我所说,这可以得到优化。例如,我们可以在_loop()方法中使用do-while循环,也可以使用Date.now()进行操作,直到我们超过50ms为止。如果我们需要更长的时间,以相同的方式创建timeout,并再次重复上述操作(因此,在50ms内进行尽可能多的操作(。

如果速度真的是个问题,我会根据每个项目的_id对数组进行预排序,至少实现一个二进制搜索,如果不是更复杂的话。

您可以尝试使用二进制搜索,在大多数情况下它比线性搜索更快。正如jAndy所说,您仍然会使用标准的for循环进行阻塞,所以请查看一些节点异步库。首先想到的是async.js

为了解决我的问题,我乱用了async.js。我已经尝试使它尽可能地可重复使用,这样它就不会只是被锁定来搜索"_id"属性。

我的解决方案:

http://jsfiddle.net/WilsonPage/yJSjP/3/

假设您可以从_id生成唯一的字符串,则可以使用js的本地对象对其进行散列。

findById = (collection, _id, callback, timeout = 500, step = 10000)->
   gen = ->
      hash = {}
      for value, i in collection
         hash[value._id] = value
         unless i % step then yield i
      hash[_id]
   it = gen()
   do findIt = ->
      {done, value} = it.next()
      if done then callback value
      else 
         console.log "hashed #{(value/collection.length*100).toFixed 0}%"
         setTimeout findIt, timeout