Overview
So I have pulled out a document from my database. Inside is a nested collection of objects. Each of the objects inside this nested collection has an '_id' attribute. I want to find one on these objects specifically by its '_id' using Javascript.
Example
/
Alternative Example
/
Questions
- Is my example the best way of achieving this?
- Will this block in Node.js?
Overview
So I have pulled out a document from my database. Inside is a nested collection of objects. Each of the objects inside this nested collection has an '_id' attribute. I want to find one on these objects specifically by its '_id' using Javascript.
Example
http://jsfiddle/WilsonPage/tNngT/
Alternative Example
http://jsfiddle/WilsonPage/tNngT/3/
Questions
- Is my example the best way of achieving this?
- Will this block in Node.js?
- The "Async Example" isn't async. I think your first example is the standard way to do it, though I'd probably return null rather than false for a not-found. – nnnnnn Commented Dec 2, 2011 at 13:05
- Checkout an example I made using async.js: jsfiddle/WilsonPage/yJSjP/3 – wilsonpage Commented Dec 2, 2011 at 15:06
5 Answers
Reset to default 1Yes, if you only know a specific value which is contained by one of your objects (which are held in an Array) you need to loop over the whole structure and pare those values.
As you also did right, break the iteration when you found one (return
in your example).
So my answer to your first question would be yes, in terms of performance this is the right and best way.
What I don't get is the "Async" example. You just moved the code and changed the structure. Your code is still "blocking" since you're using a normal for-loop
to search. If that array would be huge, it would block your node-app for the amount of time the loop needs to finish.
To really make it asynchronously, you would need to get rid of any loop
. You would need to loop over the structure with a runway-timer.
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() ));
};
And then use it like
findById( myCollection, 102, function( data ) {
console.log('MATCH -> ', data);
});
This technique (which is a simplified example), we are creating an self-invoking anonymous function and we pass in the first array item (using .shift()
). We do our parison and if we found the item we are looking for, execute a callback function the caller needs to provide. If we don't have a match but the array still contains elements (check for the .length
), we create a timeout of 25ms using setTimeout
and call our _loop
function again, this time with the next array item, because .shift()
gets and removes the first entry. We repeat that until either no items are left or we found the element. Since setTimeout
gives other tasks in the JS main thread (on a browser, the UI thread) the chance to do things, we don't block and screw up the whole show.
As I said, this can get optimized. For instance, we can use a do-while
loop within the _loop()
method and also use Date.now()
to do things until we go over the 50ms mark for instance. If we need longer than that, create a timeout
the same way and repeat the above described operation again (so its like, do as much operation as possible within 50ms).
I'd pre-sort the array by each item's _id and at least implement a binary search, if not something a little more sophisticated if speed is really an issue.
You could try using binary search, in most cases it's faster than linear search. As jAndy said, you will still block with standard for loop, so take a look at some node asynchronous library. First that falls to my mind is async.js
I messed around with async.js to produce this solution to my problem. I have tried make it a reusable as possible so it is not just locked down to search the '_id' attribute.
My Solution:
http://jsfiddle/WilsonPage/yJSjP/3/
Assuming you can generate unique strings from your _id you could hash them out with js's native object.
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
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745012084a4606131.html
评论列表(0条)