Scenario: I'm searching for a specific object in a deep object. I'm using a recursive function that goes through the children and asks them if I'm searching for them or if I'm searching for their children or grandchildren and so on. When found, the found obj will be returned, else false. Basically this:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}
i'm wondering, is there something simpler than this?:
var result = this[i].find(match_id);
if (result) return result;
It annoys me to store the result in a variable (on each level!), i just want to check if it's not false and return the result. I also considered the following, but dislike it even more for obvious reasons.
if (this[i].find(match_id)) return this[i].find(match_id);
Btw I'm also wondering, is this approach even "recursive"? it isn't really calling itself that much...
Thank you very much.
[edit]
There is another possibility by using another function check_find (which just returns only true if found) in the if statement. In some really complicated cases (e.g. where you don't just find the object, but also alter it) this might be the best approach. Or am I wrong? D:
Although the solution you have is probably "best" as far as search algorithms go, and I wouldn't necessarily suggest changing it (or I would change it to use a map instead of an algorithm), the question is interesting to me, especially relating to the functional properties of the JavaScript language, and I would like to provide some thoughts.
Method 1
The following should work without having to explicitly declare variables within a function, although they are used as function arguments instead. It's also quite succinct, although a little terse.
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};
How it works:
We test to see if this.id == match_id, if so, return this.
We use map (via Array.prototype.map) to convert this to an array of "found items", which are found using the recursive call to the find method. (Supposedly, one of these recursive calls will return our answer. The ones which don't result in an answer will return undefined.)
We filter the "found items" array so that any undefined results in the array are removed.
We return the first item in the array, and call it quits.
If there is no first item in the array, undefined will be returned.
Method 2
Another attempt to solve this problem could look like this:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
How it works:
buildObjArray builds a single, big, 1-dimensional array containing obj and all of obj's children.
Then we filter based on the criteria that an object in the array must have an id of match_id.
We return the first match.
Both Method 1 and Method 2, while interesting, have the performance disadvantage that they will continue to search even after they've found a matching id. They don't realize they have what they need until the end of the search, and this is not very efficient.
Method 3
It is certainly possible to improve the efficiency, and now I think this one really gets close to what you were interested in.
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};
How it works:
We wrap the whole find function in a try/catch block so that once an item is found, we can throw and stop execution.
We create an internal find function (IIFE) inside the try which we reference to make recursive calls.
If this.id == match_id, we throw this, stopping our search algorithm.
If it doesn't match, we recursively call find on each child.
If it did match, the throw is caught by our catch block, and the found object is returned.
Since this algorithm is able to stop execution once the object is found, it would be close in performance to yours, although it still has the overhead of the try/catch block (which on old browsers can be expensive) and forEach is slower than a typical for loop. Still these are very small performance losses.
Method 4
Finally, although this method does not fit the confines of your request, it is much, much better performance if possible in your application, and something to think about. We rely on a map of ids which maps to objects. It would look something like this:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
This way, no find method is needed at all!
The performance gains in your application by using this method will be HUGE. Please seriously consider it, if at all possible.
However, be careful to remove the object from the map whenever you will no longer be referencing that object.
delete map[obj.id];
This is necessary to prevent memory leaks.
No there is no other clear way, storing the result in a variable isn't that much trouble, actually this is what variables are used for.
Yes, that approach is recursive:
you have the base case if (this.id==match_id) return this
you have the recursive step which call itself obj.find(match_id) { ... var result = this[i].find(match_id); }
I don't see any reason, why storing the variable would be bad. It's not a copy, but a reference, so it's efficient. Plus the temporary variable is the only way, that I can see right now (I may be wrong, though).
With that in mind, I don't think, that a method check_find would make very much sense (it's most probably basically the same implementation), so if you really need this check_find method, I'd implement it as
return this.find(match_id) !== false;
Whether the method is recursive is hard to say.
Basically, I'd say yes, as the implementations of 'find' are all the same for every object, so it's pretty much the same as
function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
which is definitely recursive (the function calls itself).
However, if you'd do
onesingleobjectinmydeepobject.find = function(x) { return this; }
I'm not quite sure, if you still would call this recursive.
Related
I'm trying to recreate the functionality of the underscore _.invoke for learning purposes and I would like to really understand how it works as it seems to be something not too complicated.
The exercise is asking me to return an array with the result of calling "a" method to it. Ok, so here we start.
_.invoke = function (collection, methodName) {
let result = [];
// debugger;
if (Array.isArray(collection)) { // check if collection is an array.
for (let i = 0; i < collection.length; i++) { // iterate over collection
result.push(Array.prototype.methodName.call(collection[i]));
}
}
console.log('result:', result);
return result;
};
I don't know exactly what method is being past to methodName nor if it has any extra arguments to be forwarded (this I understand it would be used in case I'd use a method that requires args like .reduce for instance if I'm not wrong).
As I understand, when I use the .call method on methodName, it should return (push) the iterated element with the "function" applied onto it. Obviously there is something not right, I have used the debugger to see what it does on each step and once it runs the loop and arrives to the call, it quits the loop and runs to check whatever it is it does in the config file of the test.
I get this message in the error log of the HTML file:
_.invoke(mocks.arr, 'testCall').should.eql(mocks.arr);
_.invoke(mocks.obj, 'testCall').should.eql(mocks.objValuesArr);
argsArr = [mocks.arr, mocks.obj];
_.invoke(mocks.arr, 'testArgs', mocks.arr, mocks.obj);
called.should.be.true;
called = false;
argsArr = [mocks.obj, mocks.arr];
_.invoke(mocks.obj, 'testArgs', mocks.obj, mocks.arr);
called.should.be.true;
The this, thisArg and such are still a little hard for me to understand, can someone explain to me what am I missing here..?
So, after some digging, trial and error, I was totally wrong about my approach to the exercise, so I had to re-make the whole thing.
_.invoke = function (collection, methodName) {
// Spread all arguments into a variable.
let args = [...arguments];
// Since the arguments have been all passed to args, we don't need to call them as we normally would.
// Use an already defined function (_.map) with an iteratee to be passed as method.
return _.map(args[0], function (value) {
// Return the iterated value passed through the function of _.map
// and apply the rest of arguments to the element with the function from _.map if there are any.
return value[args[1]].apply(value, args.slice(2));
});
};
I don't know much about underscore.js, but I'm pretty sure _ isn't defined at all, so maybe do window._.invoke = ... instead to properly define it.
I have a function that receives a list of JS objects as an argument. I need to store information about those objects in a private variable for future reference. I do not want to stuff a property into the objects themselves, I just want to keep it out of band in a dictionary. I need to be able to lookup metadata for an object in sub-linear time.
For this I need a hash function such that, for any two objects o1 and o2,
hash(o1) !== hash(o2) whenever o1 !== o2.
A perfect example of such a hash function would be the memory address of the object, but I don't think JS exposes that. Is there a way?
Each object reference is different. Why not push the object onto an array? Traversing the array looking for an object reference might still perform better than inspecting each object in a recursive manor to generate a hash key.
function Dictionary() {
var values = [];
function contains(x) {
var i = values.length;
while(i--) {
if (values[i] === x) {
return true;
}
}
return false;
}
function count() {
return values.length;
}
function get(i) {
return (i >= 0 && i < values.length) ? values[i] : null;
}
function set(o) {
if (contains(o)) {
throw new Error("Object already exists in the Dictionary");
}
else {
return values.push(o) - 1;
}
}
function forEach(callback, context) {
for (var i = 0, length = values.length; i < length; i++) {
if (callback.call(context, values[i], i, values) === false) {
break;
}
}
}
return {
get: get,
set: set,
contains: contains,
forEach: forEach,
count: count
};
}
And to use it:
var objects = Dictionary();
var key = objects.set({});
var o = objects.get(key);
objects.contains(key); // returns true
objects.forEach(function(obj, key, values) {
// do stuff
}, this);
objects.count(); // returns 1
objects.set(o); // throws an error
To store metadata about objects, you can use an WeakMap:
WeakMaps are key/value maps in which keys are objects.
Note that this API is still experimental and thus not widely supported yet (see support table). There is a polyfill implementation which makes use of defineProperty to set GUIDs (see details here).
Javascript does not provide direct access to memory (or to the file system for that matter).
You'd probably just want to create your properties/variables within the analysis (hash) function, and then return them to where the function was called from to be stored/persisted for later reference.
Thanks everyone who chipped in to reply. You all have convinced me that what I want to do is currently not possible in JavaScript.
There seem to be two basic compromises that someone with this use case can chose between:
Linear search using ===
=== appears to be the only built-in way to distinguish between two identically-valued objects that have different references. (If you had two objects, o1 and o2, and did a deep comparison and discovered that they were value-identical, you might still want to know if they're reference-identical. Besides === you could do something weird like add a property to o1 and see if showed up in o2).
Add a property to the object.
I didn't like this approach because there's no good reason why I should have to expose this information to the outside world. However, a colleague tipped me off to a feature that I didn't know about: Object.defineProperty. With this, I can alleviate my main concerns: first, that my id would show up, unwanted, during object enumeration, and second, that someone could inadvertently alter my id if there were to be a namespace collision.
So, in case anyone comes here wanting the same thing I wanted, I'm putting it up there for the record that I'm going to add a unique id using Object.defineProperty.
I'm going through John Resig's snippets on advanced JavaScript. On #19 he mentions a method to cache the return value of a function. What's the best way to cache the return value of a function that has more than one parameter?
There has to be a much better way than stringify-ing the recieved arguments and using that as the key for the cache object:
function $$(selector, el) {
var cacheKey = JSON.stringify(arguments);
if ($$.cache[cacheKey]) return $$.cache[cacheKey];
return ($$.cache[cacheKey] = NodeListToArray( (el || document).querySelectorAll(s) ));
}
$$.cache = {};
You could use a custom hash function that can operate on objects. But hash functions cause collisions and would require significantly more code than your simple example.
Or you could make the cache n-dimensional, where n is the number of arguments. So essentially this:
function $$(selector, el) {
if ($$.cache[selector] && $$.cache[selector][el])
return $$.cache[cacheKey][el];
// etc.
That assumes that both selector and el are able to be used as object keys. You may need to stringify them in another manner.
Just consider an array element,
JSON (JavaScript Object Notation) works with generic platform, so for easy use you must create a function for your use,
Here, $$.cache[0] is your easy way after reading the cachekey,
If we make thing more easy, we might have security problem later.
I hope this will satisfy your requirement :)
EDIT:
I'm putting this at the top because ive finally figured out the actual problem.
Prototypejs is adding an Array.reduce function which is intefering with underscore (see: https://github.com/documentcloud/underscore/issues/7)
There doesn't seem to be anything conclusive here other than "use prototype > 1.6.1) but I can't control what prototype is used unfortunately. Other than altering the _.reduce method to not use the native function or proxying any method that uses reduce (see comments) I can't see any nice way of fixing this issue.
I'm having an issue with Prototypejs being included on the same page as my javascript "app" where I am using underscore.
It seems that whenever I try and use the function _.unique it is actually calling the prototype function instead, this is inside a closure and I am using requirejs to load in _. When I change the order of the libraries included so my app is included before prototype then everything works fine, unfortunately I cannot use this as a solution as I will have no control of how this is included in any page.
I was wondering if anyone had come across this problem before and had a possible solution where _.unique will always call the underscore function rather than any global prototype function called unique.
Thanks
EDIT:
I actually think I might be wrong about the unique method being overridden actually. I have just added some console logs into the underscore function and it seems it is being called but its returning empty:
_.uniq = _.unique = function(array, isSorted, iterator) {
console.log("called this");
console.log(array);
var initial = iterator ? _.map(array, iterator) : array;
var results = [];
// The `isSorted` flag is irrelevant if the array only contains two elements.
if (array.length < 3) isSorted = true;
_.reduce(initial, function (memo, value, index) {
console.log("it never gets here");
if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
memo.push(value);
results.push(array[index]);
}
return memo;
}, []);
console.log(results);
return results;
};
The first console log gives me "[1,2,3,1]" whereas the second gives me "[]". This does only seem to happen when prototype is included on the page though, so something is going on with it.
Ive added another log (it never gets here) which is ever executed. It looks like underscore is executing the "native" reduce method which is the one provided by Prototypejs which doesnt take an iterator.
Yes, Prototype.js overrides reduce, that's really a bad idea. If reduce is the only thing that Prototype.js messed, so how about set Array.prototype.reduce to null just at the start of _.uniq? Seems that _.intersection and _.union both rely on _.uniq.
_.uniq = _.unique = function(array, isSorted, iterator) {
var keepIt = Array.prototype.reduce;
Array.prototype.reduce = null;
//....
Array.prototype.reduce = keepIt;
};
In Javascript, I sometimes want to return a value from a scope that isn't the current function. It might be a block of code within the function, or it might be an enclosing function as in the following example, which uses a local function to recursively search for something. As soon as it finds a solution, the search is done and the outer function should just exit. Unfortunately, I can't think of a simpler way to do this than by hacking try/catch for the purpose:
function solve(searchSpace) {
var search = function (stuff) {
var solution = isItSolved(stuff);
if (solution) {
throw solution;
} else {
search(narrowThisWay(stuff));
search(narrowThatWay(stuff));
};
};
try {
return search(searchSpace);
} catch (solution) {
return solution;
};
};
I realize one could assign the solution to a local variable and then check it before making another recursive call, but my question is specifically about transfer of control. Is there a better way than the above? Perhaps involving label/break?
Edit: since the answers to date are variations of "ew that's bad
you're not supposed to do that", let me add some necessary
context. I'm hacking on an open-source compiler that targets
Javascript. No one is going to write this code by hand, so please
don't tell me "this is a bad programming technique". What I want is a
better code generation technique. The question is whether anyone has
any clever hack for exploiting Javascript to get more flexible control
transfer.
The reason assigning the result to a local variable and checking it is
ruled out is because that requires understanding the code in a way
that is hard for a compiler to do.
It seems I stand corrected on the intent of the question. If statements are are a useful and readable way to structure code and make it flow however you want to. There's a reason goto was taken out of so many languages, because you don't need it. And it seems like, based on your example code, you're using a try-catch block as a form of goto. If you don't want certain things to run then use if statements or equivalents:
function solve(searchSpace) {
function search = function (stuff) {
//|| will only return the first value if that value is truthy, subsequent values will be ignored
return isItSolved(stuff) || (search(narrowThisWay(stuff)) || search(narrowThatWay(stuff)));
};
return search(searchSpace);
};
I know of no way to break out of function calls like you want. You can break out of loops using labels, but it doesn't seem that's much help to your situation. Other than that, I don't think JavaScript has any such ability beyond your use of exceptions
function solve(stuff) {
return isItSolved(stuff) || solve(narrowThisWay(stuff)) || solve(narrowThatWay(stuff));
}
Bob's way is good... exept that he uses twice the function statement (and that he uses ; after a function delaration without an assignment)... and that as we can do it that way, function solve actually is function search.
PS : This code will epically fail if the isItSolved, narrowThisWay or narrowThatWay functions can return a value evaluated to false as a positive result. In this cas, you would have to use ? : statement in order to check if all responses are !== undefined.
PS2: And of ourse, if these function can send an error, you have to catch it...
It looks like you're doing a fairly straightforward recursive search in your example. Why not just use "return"?
function solve(searchSpace) {
var search = function (stuff) {
var solution = isItSolved(stuff);
if (solution) {
return solution;
} else {
solution = search(narrowThisWay(stuff));
if (solution) {
return solution;
}
return search(narrowThatWay(stuff));
};
};
return search(searchSpace);
};
I suppose it could be that there are other constraints you haven't mentioned, but it's in general possible to turn any control flow into a set of nested (or recursive) functions, with appropriate return values.
The cleanest way would be to use a continuation, but you don't have that efficiently in JS (a few JS engines support continuations, but for the rest there's only CPS, which cries out for tail calls). In C, you could use setjmp/longjmp. In Common Lisp, you could use conditions (which include the functionality of exceptions plus much more). In JS, exceptions are the only non-local control flow option you have available.
You can programmatically transform a program into another that uses CPS.
function solve(searchSpace, isItSolved, isBase, narrowThisWay, narrowThatWay) {
function search(stuff, k) {
solution = isItSolved(stuff);
if (solution) {
return solution;
} else if (isBase(stuff)) {
return k();
} else {
return search(narrowThisWay(stuff), function() {
return search(narrowThatWay(stuff), k);
});
};
};
return search(searchSpace, function(val) {return val});
};
var arr=[1, 2,9,72,0,34,5,33,24,62,89,90,30,54,590,23,59,62,73];
solve(arr, function(a) {return (a.length==1 && a[0] == 5) ? a[0] : false;},
function (a) {return a.length < 2; },
function (a) {return a.slice(0, a.length / 2);},
function (a) {return a.slice(a.length / 2);}
);