Algorithms for Directed Cyclic Graph Traversal (JavaScript) - javascript

I have a connected, directed, cyclic graph. The task is to discover every single node in the graph without falling into an infinite loop, as a regular tree traversal algorithm will do.
You can assume that I already know what node to start at so as to reach all points in the directed graph, and that for each node I have a function that will return the nodes it directs to. Is there a known algorithm for finding all nodes?
The main issue is really avoiding cycles, and I would love it if there was a way to do that without keeping track of every single node and comparing it with a list of nodes that has already been traversed.
If you need more details, the actual task is to get a list of every named function in JavaScript, including functions that are properties of other objects. So I tried something like the following, as I thought the JS objects' references to each other made a tree (but of course it doesn't):
function __findFunctions(obj){
for (var f in obj){
// for special case of edge with self
if (obj === obj[f]){
continue
}
if (typeof obj[f] === 'function' &&
obj.hasOwnProperty(f) &&
// exclude native functions, we only want user-defined ones
!(/\[(native\scode|object\sfunction)\]/i).test(obj[f].toString()) &&
// exclude functions with __ prefix
!(/^\s*function\s*__/).test(obj[f].toString())
){
document.write(f + "<br/>" + obj[f].toString() + "<hr/>");
}
//alert(typeof obj[f] + "\n" + obj + "\n" + obj[f] + "\n" + f)
__findFunctions(obj[f]);
}
}
__findFunctions(window);
The problem with this code is that it gets stuck in cycles.

I would love it if there was a way to do that without keeping track of every single node and comparing it with a list of nodes that has already been traversed.
It may not be as bad as checking a list of already-traversed nodes. You could, instead, give each node a unique ID of some sort:
// psuedo
id=0;
for each node
node.id = id++;
etc.
Then you can add each node's ID to a hash while you traverse:
var alreadyTraversed = {};
// Traversing a node:
alreadyTraversed[node.id] = true;
And later on, when you need to check whether or not its already been traversed:
if (node.id in alreadyTraversed) // It's already been traversed...
Or, for a really rudimentary solution, simply set some property on each object that you traverse:
node._traversed = true;
// Later:
if (someNode._traversed) // already traversed.

You would need to maintain a list of already visited nodes if you want to avoid cycles.
E.g.
__findFunctions(obj, visited) as your signature
at start do an "in array" test for current obj and return if so.
at start add obj to the visited for subsequent traversals.

Related

getcomputedstyle only the changes from default

The code I currently have gets the whole CSS, even the default one. What I want is to get only the CSS changed from default.
function baba() {
addEventListener("mouseover", function() {
var elem = document.getElementById("ex");
cssObj = window.getComputedStyle(elem, null)
var txt = "";
for (i = 0; i < cssObj.length; i++) {
cssObjProp = cssObj.item(i)
txt += cssObjProp + " = " + cssObj.getPropertyValue(cssObjProp) + "<br>";
document.getElementById("empty").innerHTML = txt;
}
})
}
<p id="ex" onclick="baba()">Hello World</p>
<h1>Hello World</h1>
<p id="empty"></p>
Okay, so here's how I'd tackle it. Note: I just slammed this out in the console in 5 minutes; I'm sure there are more efficient ways to handle it, but for PoC this should get you going.
Requirements Analysis
Really (barring a more specific edge-case application, anyway), what you're asking for is "How does Element <XXX>'s current computed style differ from a vanilla object of the same type in the same context?" It's nonsensical to ask how it differs from "default" because "default", perforce, is going to be influenced by said context (don't agree? Wait for it; I'll 'splain).
Because of this, really what we need to be examining is a <XXX> that lacks the effects applied to your target object (consequences of its DOM position, class, id, attributes, predecessors, etc.). The good news is: we can totally fake it! Check it out:
The Setup
First thing's first, let's get hold of our object. This would be better executed as a function, I know, but for illustrative purposes, work with me here. Let's pick an object you can see the results on right away. Let's see... how about the Search bar at the top of this very page? Hit f12 to pop your console, and you'll see it's name is 'q'. That'll work.
// Get the StackOverflow Search field from the top of this page.
var targetDOMElement = document.querySelector('[name="q"]');
// Now, let's get its current style snapshot.
var targetObjsStyles = window.getComputedStyle(targetDOMElement);
// ... and vomit it out to our console, just so we know what we're dealing with.
console.log('ORIGINAL SET (' + Object.keys(targetObjsStyles).length + ' rules):',targetObjsStyles);
Capital! Now we have our source object (our "<XXX>", if you will).
The Control
Next, we need something to compare it against. Now, being the obedient little boy who was raised Orthodox Scientist that I am, in my mind that's a control. Fortunately, we know plenty about our source object, so let's manufacture one:
// Create a new element of the same type (e.g. tagName) as our target
var tempCopyOfTarget = document.createElement(targetDOMElement.tagName);
// Insert it into the context AT THE BEGINNING of the page. Both bits here are important:
// if we create it within a documentFragment (try it) literally every property will
// be flagged as unique. I suspect this has to do with the client's default
// renderer, vs. the purity of a abstracted prototype, but I won't lie: I'm guessing.
// It MUST be at the start of the body to avoid silliness like
// body > .first-element ~ xxx { display:none; }
// CSS still won't let us target predecessors/ancestors, alas.
document.body.insertAdjacentElement('afterBegin', tempCopyOfTarget);
// Now our new object shares our target's context, get ITS snapshot.
var basicElementsCSS = window.getComputedStyle(tempCopyOfTarget);
console.log('BASELINE (DUMMY OBJECT) SET (' + Object.keys(basicElementsCSS).length + ' rules):',basicElementsCSS);
The Grunt Work
While I'm certain most folks see where I'm going at this point, let's finish her off. Given a testable quantity, and a control, check for deltas.
// Create an empty object to store any changes in.
var cleanSetOfStyles = {};
// Objectify our target's style snapshot, and iterate.
Object.entries(targetObjsStyles).forEach(p=>{
// If a key-value pair exists that matches our control, ignore it. Otherwise,
// tack it onto our clean object for later perusal.
if(basicElementsCSS[p[0]] !== p[1]){
cleanSetOfStyles[p[0]] = p[1];
}
});
Awesome! Nice work!
Conclusion
Now, assuming my hypothesis is correct, we should see within our clean object a set of properties and their corresponding values. The length of this list should be both non-zero, and different than the count contained within the raw sets above (which, the more observant of you will have noticed, WERE the same, in that the browser assigns ALL possible styles' values to an object when a getComputedStyles collection is requested.
// Display our deltas
console.log('CLEAN SET (' + Object.keys(cleanSetOfStyles).length + ' rules):',cleanSetOfStyles);
// Oh, and always remember to clean up after you make a mess in the lab.
tempCopyOfTarget.remove()
What's this!? VICTORY! At least in my environment (which has to factor my browser make, version, active plug-ins, supported features, operating system, etc., etc.; your mileage may vary), I count 116 rules that remain and are acting on our target object. These are the rules that differ from our vanilla, first-line-of-code object we summoned into being for the picoseconds it took the browser to take a gander at it.
CAVEATS
There's always a catch, isn't there?
This is NOT a foolproof system. I can think of a half dozen ways this will fail off the top of my head (:empty modifiers, depending on the scope you're in... [name="q"] ~ [name="q"] rules, the insertion of our dummy object now making apply to our target... :first-of-type no longer being applicable... all kinds of 'whoopsies'). BUT, I'm prepared to assert all the ones I can think of are both edge cases, and themselves manageable, given proper forethought.
TLDR
Here's the whole code, copy+pasteable directly into console, if you're so inclined, and sans comments:
var targetDOMElement = document.querySelector('[name="q"]');
var targetObjsStyles = window.getComputedStyle(targetDOMElement);
console.log('ORIGINAL SET (' + Object.keys(targetObjsStyles).length + ' rules):',targetObjsStyles)
var tempCopyOfTarget = document.createElement(targetDOMElement.tagName);
document.body.insertAdjacentElement('afterBegin', tempCopyOfTarget);
var basicElementsCSS = window.getComputedStyle(tempCopyOfTarget);
console.log('BASELINE (DUMMY OBJECT) SET (' + Object.keys(basicElementsCSS).length + ' rules):',basicElementsCSS)
var cleanSetOfStyles = {};
Object.entries(targetObjsStyles).forEach(p=>{
if(basicElementsCSS[p[0]] !== p[1]){
cleanSetOfStyles[p[0]] = p[1];
}
});
console.log('CLEAN SET (' + Object.keys(cleanSetOfStyles).length + ' rules):',cleanSetOfStyles);
tempCopyOfTarget.remove()
Final note: I know this question is a couple months old, but nobody really answered it outside of "Nope! You're screwed!"
On the off chance #angels7 still needs the fix, here ya go. Otherwise, "Hi, all you far-out future folk!"

Creating a Stack like behavior is JavaScript/Node

So I have a block of code that looks like this:
stack.forEach(function(element){
//sys.puts(sys.inspect(element, false, null));
console.log('-----element start-----');
console.log(element.type + ':' + element.raw);
console.log('-----element end-----');
if(element.children){
element.children.forEach(function(childElement){
stack.push(childElement);
});
}
});
The issue is that this is not behaving like I would except a stack to behave and wondering if this in an issue with JavaScript itself. The issue I am seeing if that when I call the stack.push for each child to add it to the stack for processing, the initial stack.forEach() does not seem to be picking it up and it is only logging to top level elements. If I try to do another stack.forEach() directly after this one, it then displays the next level of child elements so I know .push is diffidently adding the child elements to the stack.
It almost seems like .forEach() grabs the data as it is and does not get an updates if they happen within the foreach. Is this true for JavaScript? Is there a different way I can accomplish the same thing (be able to process a top level element and all levels of children elements below it)?
Your supposition is correct. The ForEach function takes a snapshot of the array and any updates will not be processed.
You are trying to implement a classic tree traversal algorithm which can most easily be coded as a recursive function as follows :
var stack = [];
function traverse (element) {
//sys.puts (sys.inspect(element, false, null));
console.log ('-----element start-----');
console.log (element.type + ':' + element.raw);
console.log ('-----element end-----');
stack.push (element); // for preorder traversal (parent before children)
if (element.children)
element.children.forEach (traverse);
// stack.push (element); // for postorder traversal (children before parent)
};
traverse (document.body); // for example
Yes, .forEach does only iterate over the range the array initially has, it works with a copy of the .length value. If you want to change that behavior, I'd recommend a classical loop which queries the length property each time:
var stack = […];
for (var i=0; i<stack.length; i++) {
// process stack[i]
stack.push(…);
}
or even a more list-like behavior with
var stack = […];
while (stack.length) {
var cur = stack.shift();
// process cur
stack.push(…); // with variable arguments, you want to stop somewhen :-)
}
If you don't want/need to maintain the stack yourself, a recursive function is the proper choice for tree traversal.

Improve performance of searching JSON object with jQuery

Please forgive me if this is answered on SO somewhere already. I've searched, and it seems as though this is a fairly specific case.
Here's an example of the JSON (NOTE: this is very stripped down - this is dynamically loaded, and currently there are 126 records):
var layout = {
"2":[{"id":"40","attribute_id":"2","option_id":null,"design_attribute_id":"4","design_option_id":"131","width":"10","height":"10",
"repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"},
{"id":"41","attribute_id":"2","option_id":"115","design_attribute_id":"4","design_option_id":"131","width":"2","height":"1",
"repeat":"0","top":"0","left":"0","bottom":"4","right":"2","use_right":"0","use_bottom":"0","apply_to_options":"0"},
{"id":"44","attribute_id":"2","option_id":"118","design_attribute_id":"4","design_option_id":"131","width":"10","height":"10",
"repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"}],
"5":[{"id":"326","attribute_id":"5","option_id":null,"design_attribute_id":"4","design_option_id":"154","width":"5","height":"5",
"repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"}]
};
I need to match the right combination of values. Here's the function I currently use:
function drawOption(attid, optid) {
var attlayout = layout[attid];
$.each(attlayout, function(k, v) {
// d_opt_id and d_opt_id are global scoped variable set elsewhere
if (v.design_attribute_id == d_att_id
&& v.design_option_id == d_opt_id
&& v.attribute_id == attid
&& ((v.apply_to_options == 1 || (v.option_id === optid)))) {
// Do stuff here
}
});
}
The issue is that I might iterate through 10-15 layouts (unique attid's), and any given layout (attid) might have as many as 50 possibilities, which means that this loop is being run A LOT.
Given the multiple criteria that have to be matched, would an AJAX call work better? (This JSON is dynamically created via PHP, so I could craft a PHP function that could possibly do this more efficently),
or am I completely missing something about how to find items in a JSON object?
As always, any suggestions for improving the code are welcome!
EDIT:
I apologize for not making this clear, but the purpose of this question is to find a way to improve the performance. The page has a lot of javascript, and this is a location where I know that performance is lower than it could be.
First and foremost you should measure and only act if there is a real performance concern. You need exact numbers like 200ms or 80% time is spent there. "It runs a lot" doesn't mean anything. The browser can loop very fast.
You can improve constant factors as others mentioned, like using a native for loop instead of jQuery.each. Chaching global variables won't help you too much in this case.
If you really want to improve efficency you should find a better algorithm than O(n). Assuming that you only use this data for finding elements matching a certain criteria you can use JS objects as hashes to achive a O(1) performance.
Just an example for you specific case:
var layout = {
"2": { "2,,4,131,10,0": ["40", "93"], "2,115,4,131,0": ["41"] },
"4": { ... },
...
};
It's fairly easy to generate this output in php, and then you just use a lookup to find ids matching your particular criteria.
IMHO, a simple hashmap index will probably work best. This does require you to loop over the data ahead of time, but the index can be easily appended to and cached.
Once the index is generated, this should be O(1) for lookups, and will handle multiple entries per key.
var layout = {
"2":[[...], ...],
"5":[[...], ...]
};
var index = {};
function makeKey(data) {
return data.join('_');
}
for(var l in layout) {
var cur = layout[l];
for(var i in cur) {
var item = cur[i];
var key = makeKey([item.p1, item.p2, ...]);
index[key] = index[key] || [];
index[key].push(item);
}
}
function find(attid, optid) {
var key = makeKey([attid, optid, 1, d_att_id, ...]);
return index[key]; //this is an array of results
}
My first suggestion would be to stop using $.each if you want to squeeze out every bit of performance you can. jQuery.each does a bit more than a traditional loop. Take a look at this jsFiddle with your browser's debugger running (i.e. Safari's/Chrome's web developer tools) and step through the fiddle until execution fully returns from jQuery.
For reference, the fiddle's code is:
var myArr = [{"foo":"bar"},{"answer":42}];
debugger;
$.each(myArr, function(k, v) {
console.log(k + ': ');
console.dir(v);
});​
Now run through the second version:
var myArr = [{"foo":"bar"},{"answer":42}],
i, j;
debugger;
for (i = 0, j = myArr.length; i < j; i += 1) {
console.log('myArr[' + i + ']: ');
console.dir(myArr[i]);
}​
Notice that there are far fewer operations being executed in the second version. So that's a tiny bit of performance gain.
Second, eliminate as many lookups outside of the local scope as you can. Even if you cache a reference to your global variables (boo!) then you can save a lot of time given that this loop will be executed possibly hundreds of times. So instead of:
function foo(a, b) {
if (a === globalA && b === globalB) {
// Do stuff
}
}
You'd do:
function foo(a, b) {
var gA = globalA,
gB = globalB;
if (a === gA && b === gB) {
// Do stuff
}
}
As for pairing down the conditional based on the object members, I'm not seeing much else that could be improved. The object properties you are checking are top level, and you're looking at local instances of each object (so the scope chain lookups are short).
Without knowing more about how this is actually supposed to work, those are the best recommendations I can make. However, I can make the guess that your idea of starting with simpler JSON data would be a big improvement. If you know what the layout, and its constraints, is, then requesting the specific details from the server would mean you don't have to check so many conditions. You could simply ask the server for the details that you actually need to implement and loop through those (to update the DOM or whatever).
I see that the you are searching by
5 fields: v.design_attribute_id,v.design_option_id,v.attribute_id,v.apply_to_options,v.option_id.
What you could do is add an extra field to the objects called "key" that is a composite of the values in those fields.
Here's an example
{
"key": "4_131_2_0_0" //i picked 0 to represent null, but you can pick any number
"id": "40",
"attribute_id": "2",
"option_id": null,
"design_attribute_id": "4",
"design_option_id": "131",
"width": "10",
"height": "10",
"repeat": "0",
"top": "0",
"left": "0",
"bottom": "0",
"right": "0",
"use_right": "0",
"use_bottom": "0",
"apply_to_options": "0"
}
Note though that you must normalize the length of each value.
Meaning that if one objects optionId is 1 and another object optionID is 566 you must represent
the first optionId as 001 in the key string.
With this field you can then sort the array on the server side before returning it to the client.
Then you can use a binary search to find the values on the client.
Using the binary search implementation located here
http://www.nczonline.net/blog/2009/09/01/computer-science-in-javascript-binary-search/
Your search function would look something like
function drawOption(attid, optid) {
var attlayout = layout[attid];
var needle = d_att_id + "_" + d_opt_id + "_" + attid + "_" + optid; //remember to normalize length if you have to
var idx = binarySearch(attlayout,needle);
var item;
if(idx !== -1){
item = attlayout[idx];
//do something
}
}
Another method you can try using this composite key idea is to have the server return
the layout objects in one big object mapped by
attid,v.design_attribute_id,v.design_option_id,v.attribute_id,v.apply_to_options,v.option_id
Then you can look up in O(1) time.
It would look something like
function drawOption(attid, optid) {
var needle = attid + "_" + d_att_id + "_" + d_opt_id + "_" + attid + "_" + optid; //remember to normalize length if you have to
var item = layout[needle];
if(typeof item !== "undefined"){
//do something
}
}
When trying to improve your code, it is always better to check which functions are taking time using firebug profiling. You can either profile by clicking on profile button in firebug's console panel and then run your code or using firebug's profiling commands in your code
From the code that you have given, only a few improvement points can be given.
$.each is slow compared to native looping solutions. For the best
looping solutions, check out this JsPref test
It would be better to change the JSON to use arrays instead of object literals. It is said to be more faster to retrieve values.
I have experience to such issue before, my js array of objects consist of 8 thousands record and more.
My experience is not about the performance, but the readability, maintainability, scalable of the codes.
hence I developed an JS Object Query Library 2 years ago: JSOQL
http://code.google.com/p/jsoql/
It works like SQL to allow you query your js array of objects with syntax similar to SQL.
The example usage is something like this, I cant really remember, but you can download the example usage in the download tab.
new JSQOL().Select([field1, field2, field3 ...]).From({ ... }) .Where(fn) .Offset(int) .Limit(int) .Get();
Note:
{...} is your array of objects, or an object it self.
Hope it helps, you can send me message if you need more information.
It's not going to work everywhere but your problem sounds like something that can be done with webworkers
Another thing I would look at if you dont have webworkers is trying not to block the ui to long. If you can chunk it into bits of about 40ms and then setTimeout the next chunck for just a few ms later the user will have a more pleasant experience. This needs a little fiddling but users will start to notice stuff when something takes longer than somewhere between 50 and 100ms
Have you considered using the jQuery grep function?
jQuery grep
And jquery grep on json object array for an example.
Here is one technique that will probably yield better performance at the expense of using a bit more memory.
I'll leave my code examples simple just to illustrate the concept.
First, you'll want to pre-process your JSON data into some additional arrays that act as indexes. Here is an example of what the final arrays might look like after pre-processing:
var layouts_by_attribute = {
// attribute_id => array(layouts)
2: [40, 41, 44],
5: [326]
};
var layouts_by_design_attribute_id = {
// design_attribute_id => array(layouts)
4: [40, 41, 44, 326]
};
Finding a layout by attribute is now very quick:
function findByAttribute(attribute_id) {
return layouts = layouts_by_attribute[attribute_id];
}
function findByDesignAttribute(design_attribute_id) {
return layouts = layouts_by_design_attribute[design_attribute_id];
}

Is setting element.id as a side effect bad practice?

I'm writing a little cached function in a plugin / library. It takes a HTMLElement and returns a Decorator.
return function _cache(elem) {
if (elem.id === "") {
elem.id = PLUGIN_NAME + "_" + uid++;
}
if (cache[elem.id] === void 0) {
cache[elem.id] = _factory(elem);
}
return cache[elem.id];
}
Here I'm storing some expensive operation in a cache by the id of the HTMLElement. This is a O(1) lookup but it uses the "bad practice" of setting elem.id and having a side effect.
The alternative would be O(N) lookup on the cache
return function _cache(elem) {
for (var i = 0, ii = cache.length; i++) {
var o = cache[i];
if (o.elem == elem) return o.data;
}
var ret = _factory(elem);
cache.push({ elem: elem, data: ret });
return ret;
}
But this means that my cached expensive method doesn't have any side effects on the HTMLElement.
Question:
Is this "side effect" innocent and is it worth doing for the optimization on my decorator?
Real Code:
Gist of plugin template where I use this snippet
Edit:
I'm clearly too tired and forgot data-foo exists. Here's how it should be implemented
var attr = "data-" + PLUGIN_NAME + "-cache";
return function _cache(elem) {
var val = elem.getAttribute(attr);
if (val === null || val === "") {
val = PLUGIN_NAME + "_" + uid++;
elem.setAttribute(attr, val);
}
if (cache[val] === undefined) {
cache[val] = _factory(elem);
}
return cache[val];
}
Instead of using the id, use data-x - that's what it was created for.
id has a specific meaning, is confusing to see it automagically generated (even if properly documented, which is nearly never.) You're also risking a slight chance of override.
Is this "side effect" innocent
No, clearly. Is it going to interact well with other scripts on the page? Don't know... that depends what it's for and what other kinds of scripts you expect it to be combined with. You can never make a ‘plugin’ that won't ever fail when interacting with other plugins and scripts, but by keeping the side-effects to a minimum you can at least try to minimise it.
Note that id is not a unique identifier. Although there should be only one element with a given ID in a document at one particular time, (a) multiple elements might be created with the same ID and inserted into the document sequentially (perhaps one element replacing another with the same ID), and (b) people still do use duplicate IDs even though it's wrong. Either would cause your cache to collect old, no-longer-used elements and return them inappropriately.
It is unfortunate that there is no JavaScript function to get a scalar/hashable unique identifier for an arbitrary object; the only way to obtain object identity is to ===-compare against other objects.
Another common way forward is to add an arbitrary new property to the node (‘expando’ in IE terms), with a randomised really-unique ID. Expandos aren't guaranteed by standard to work, but it has worked in all browsers back to day one and is commonly used.
This is how for example jQuery identifies elements uniquely, and if you are writing a plugin for jQuery you might try taking advantage of that—jQuery.expando holds the name of the arbitrary expando property being used for this purpose... or, sticking within the documented featureset, data() could be used to add your own metadata to the element including another unique ID of your own.
Expandos do have some unpleasant side-effects including accidentally treating them as attributes in IE<9 (which can't tell the difference between properties and attribute), but if you're using jQuery anyway you probably don't have anything to lose.
is it worth doing for the optimization on my decorator?
Depends how many you're expecting to have on a page. Comparing each item to each other item is an O(n²) operation; tolerable (and probably preferable given the side-effects) if n is low, but quickly getting unmanageable as n grows.

swapping nodes in a liveset of nodes

This has been a challenge for me...
I have a set of nodes in an XML doc. I need to sort them based on a certain node value. So if I iterate through the nodes, and then the node value matches my criteria, I want it to go to the end.
Problem is, of course as soon as I swap, as nodes are in a live set, the iteration pointer misses one entry of course, as the appendChild is operating on a live-set.
This is my code so far, but as I said, it may miss an entry due to the swapping:
for (var i=1; i <= nElem; i++)
{
var node = getNode(dom,"//item[" + i + "]");
var state = getNodeValue(dom,"//item[" + i + "]/state");
if ((state != 'XX') && (i != nElem))
{
node.parentNode.appendChild(node);
}
}
What I actually want is that all items in state "XX" are at the top.
Has anyone an intelligent idea to this?
Thanks
You could use array.sort() and pass a custom sort routine:
var nodes = getNode(dom, "//item"); gets you an array of items
next, remove the entries in nodes from the dom
do an nodes.sort(sortfunction) where sortfunction is sortfunction(a,b)
implement sortfunction so that it returns
-1 if a shall be lower than b
0 if equal
1 if a shall be higher than b
add the entries of nodes back to the dom
I think, that would do it (as long as I'm not missing something).

Categories

Resources