extract subsets of an array-like object

G

Gabriel Gilini

Hi, I came to the need of extracting the 'href' property from the
StyleSheet objects contained within document.styleSheets object. So,
as a (lame) attempt to build something like CakePHP's Set::extract
function[1], which uses XPath sintax to extract subsets of an array, I
came up to this:

function extract(path, iterable){
if((typeof iterable.length != 'number') || (path.indexOf('/') !==
0)){
return false;
}
if(path == '/'){
return iterable;
}
var filtered = [];
var arrLikePath = '["' + path.slice(1).replace('/', '"]["', 'g') +
'"]';
var getVal = new Function('return arguments[0]' + arrLikePath);
for(var i = 0, len = iterable.length, tmpVal; i < len; ++i){
try{
tmpVal = getVal(iterable);
filtered.push(tmpVal);
}
catch(err){}
}
return filtered;
}

It's obviously far from a XPath 'selector', but it allows me to do
something like - extract('/href', document.styleSheets) - which is
fine for now.

After doing this, I realized that this function could be useful when
dealing with JSON-formatted data from some API's, and now I'd like to
make it efficient.

My main problem now is determining if a certain property exists in an
object, independent of its depth. For my lame extract function I used
the try/catch statement, but with large data sets where a lot of - foo
is undefined - exceptions can be thrown, the execution gets
ridiculously slow.

I thought of making a helper function which would recursively check
for the properties, but it just feels like this should be simpler.
Anyway, I'll do it and post the results here, just in case anyone is
interested. I'm using the following document to test it:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Extract Test</title>
<script type="text/javascript" src="extract.js"></script>
<script type="text/javascript">
function appendText(string, father){
var pE = document.createElement('p');
var tN = document.createTextNode(string);

pE.appendChild(tN);
father.appendChild(pE);
}

window.onload = function(){
var r = document.getElementById('results');
var foo = [];
for(var i = 0; i < 1000; ++i){
if(i % 2){
foo = {'bar': 'bar'};
}else{
foo = {'bar': 'bar', 'baz': {'theAnswer':
42}};
}
}
var dt = new Date;
var res = extract('/baz/theAnswer', foo);
dt = (new Date) - dt;
appendText(dt + 'ms', r);
appendText("result array length: " + res.length , r);
}
</script>
</head>
<body>
<div id="results">
</div>
</body>
</html>

With 500 errors thrown, the code takes 1,5 seconds to execute. I'm
pretty sure that any kind of recursive checking function would top
that for a two-level-deep subject.

Any thoughts about this are welcome.

[1] http://book.cakephp.org/view/671/extract
 
G

Gabriel Gilini

Ok, I removed that inefficient and unneeded - try/catch - block and
also the - new Function - thing. The getPropertyRecursively function
seems to perform just fine and the execution time lowered to ~15ms.

New code:
function extract(path, iterable){
if((typeof iterable.length != 'number') || (path.indexOf('/') !==
0)){
return false;
}
if(path == '/'){
return iterable;
}
var filtered = [];
var props = path.slice(1).split('/');
for(var i = 0, len = iterable.length, tmpVal; i < len; ++i){
tmpVal = getPropertyRecursively(props.slice(), iterable);
if(tmpVal){
filtered.push(tmpVal);
}
}
return filtered;
}

function getPropertyRecursively(props, obj){
if(props.length === 1){
return obj[props];
}

var prop = props.shift();
if(obj[prop]){
return getPropertyRecursively(props, obj[prop]);
}
}

I don't have any old browsers handy to test that, but it seems to work
fine on Firefox 3.0.8, Konqueror 3.5 and Opera 9.6. If there's
anything that could be improved in the code above I'd gladly hear it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top