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

概观

所以我已经从我的数据库中取出了一个文件。 里面是对象的嵌套集合。 这个嵌套集合中的每个对象都有一个“_id”属性。 我想使用Javascript在其中find一个关于这些对象的“_id”。

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

替代例子

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

问题

  1. 我的例子是实现这个目标的最好方法吗?
  2. 这将在Node.js块?

是的,如果你只知道你的一个对象(包含在一个数组中)中包含的特定值,则需要遍历整个结构并比较这些值。

正如你也做对了,当你发现一个(在你的例子中return )打破迭代。
所以我对你的第一个问题的回答是肯定的 ,在performance方面这是最好的方法。

我没有得到的是“ asynchronous ”的例子。 您只是移动了代码并更改了结构。 你的代码仍然是“阻塞”,因为你正在使用一个正常for-loop来search。 如果这个数组是巨大的 ,它会阻止你的节点应用程序的循环需要完成的时间量。

要真正使它asynchronous,你需要摆脱任何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); }); 

这个技巧(这是一个简化的例子),我们正在创build一个自我调用的匿名函数,我们传递第一个数组项(使用.shift() )。 我们做比较,如果我们find了我们正在寻找的项目,执行调用者需要提供的callback函数。 如果我们没有匹配,但数组仍然包含元素(检查.length ),我们使用setTimeout创build了25ms的超时时间,并再次调用我们的_loop函数,这次是下一个数组项,因为.shift()获取并删除第一个条目。 我们重复这个,直到没有剩下的东西或者我们find了这个元素。 由于setTimeout在JS主线程(在浏览器, UI线程 )中提供了其他任务的机会,所以我们不会阻塞和搞砸整个表演。

正如我所说,这可以得到优化。 例如,我们可以在_loop()方法中使用一个do-while循环,并使用Date.now()来做事情,直到我们超过50ms标记为止。 如果我们需要比这更长的timeout按照同样的方法创build一个timeout ,并重复上述操作(如此,在50ms内尽可能多地操作)。

我会预先对每个项目的_id进行数组sorting,并且至less实现一个二进制search,如果速度确实是一个问题,那么不是更复杂一点。

您可以尝试使用二进制search,在大多数情况下,它比线性search更快。 正如jAndy所说,你仍然会用标准for循环来阻塞,所以看看一些节点的asynchronous库。 首先,我认为是async.js

我搞砸了async.js产生这个解决scheme,我的问题。 我试图使它可以重用,所以它不是只是lockingsearch“_id”属性。

我的解决scheme

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

假设你可以从你的_id生成唯一的string,你可以用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