Búsqueda recursiva de un valor en variables globales y sus propiedades

Let's say that I want to search for a value, like 'StackOverflow', in all declared variables in window. I can do it with this code:

function globalSearch(obj, value) {
    for(var p in obj)
        if(obj[p] == value)
            return(p);
}
globalSearch(window, 'StackOverflow');

This code will return the name of a variable that have this value (or returns nothing). So, if I have declared a variable with value 'StackOverflow', it will successfully find it.

My problem is that I want to go deeper and search thru window's objects (and its own nested objects) too, to achieve a result like this:

var x = 'StackOverflow'                     // returns 'x'
var y = { a : 'StackOverflow' }             // returns 'y.a'
var z = { a : { b: 'StackOverflow' } }      // returns 'z.a.b'

I'm having problems with inherited methods of Objects. Is there a way to do this?

preguntado el 24 de agosto de 12 a las 03:08

What so you mean by problems with inherited methods? -

4 Respuestas

Deep search but without the recursive function calls

Functional recursion has internal stack limits and wastes memory.

Características adicionales añadidas

Recursive object protection in the form of a searched array; It doesn't use up too much memory of course as the objects are only stored as references.

Return true if the the object itself matches the value. Otherwise it would return '' which would match to false.

Arrays use angle-bracket notation.

El código

function globalSearch(startObject, value) {
    var stack = [[startObject,'']];
    var searched = [];
    var found = false;

    var isArray = function(test) {
        return Object.prototype.toString.call( test ) === '[object Array]';
    }

    while(stack.length) {
        var fromStack = stack.pop();
        var obj = fromStack[0];
        var address = fromStack[1];

        if( typeof obj == typeof value && obj == value) {
            var found = address;
            break;
        }else if(typeof obj == "object" && searched.indexOf(obj) == -1){
           if ( isArray(obj) ) {
              var prefix = '[';
              var postfix = ']';
           }else {
              var prefix = '.';
              var postfix = '';
           }
           for( i in obj ) {
              stack.push( [ obj[i], address + prefix + i + postfix ] );
           }
           searched.push(obj);
        }
    }
    return found == '' ? true : found;
}

Problemas

Without passing the initial variable name into the function, we can't return the fully qualified variable name from the beginning. I can't think of a solution and I would be surprised if there was one.

Variable names with spaces are valid as the key to an object, as are other invalid variable names, it just means that the value must be addressed using angle-brackets. There are a couple of solutions I can think of. Regex check each variable name to make sure it's valid and use angle-brackets notation if it is not. The overriding problem with this is that the reg-ex is a page long. Alternatively, we could only use angle-brackets but this isn't really true to the OPs original question.

The indexOf call on the array 'searched' might be a bit heavy on very large objects but I can't yet think of an alternative.

Mejoradas

Apart from cleaning up the code a little, it would also be nice if the function returned an array of matches. This also raises another issue in that the returned array would not contain references to recursive objects. Maybe the function could accept a result format configuration parameter.

respondido 10 nov., 21:21

Running this on virtually any page in the firebug console, I get "The operation is insecure." I'm messing around with some error-catching schemes, but I'm not sure I'll find anything good enough to add here. Maybe someone else can make an improvement that gets around this? - eterna nueva

Doing it with window: Uncaught DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules at globalSearch (<anonymous>:27:32) - obeliksz

Here's a gist with an improved version of your code: gist.github.com/stracker-phil/e5b3bbd5d5eb4ffb2acdcda90d8bd04f The function searches all occurrences and stores them in an array, also it additionally allows searching for key-names, regex searches, and can better handle DOM nodes - Felipe

Uncaught TypeError: Illegal invocation usar window and the latest version of Canary. - jose unger

This should work. It uses recursion to achieve the result.

function globalSearch(obj, value) {
    for(var p in obj)
        if(obj[p] == value){
            return(p);
        }else if(typeof obj[p] == "object" && obj[p] != obj){
           var te = globalSearch(obj[p], value);
           if(te!=false){ return p + "." + te }
        }
    return false;
}

Respondido 24 ago 12, 03:08

Doing it for window: Uncaught RangeError: Maximum call stack size exceeded at Function.valueOf (<anonymous>) - obeliksz

Make your solution recursive. If you have an object, call your function again.

function globalSearch(obj, value) {
    for(var p in obj) {
        if (obj[p] == value) {
            return(p);
        } else if (typeof obj[p] === "object") {
            var recursiveCheck= globalSearch(obj[p], value);
            if (recursiveCheck) {
                return p + "." + recursiveCheck;
            }
        }
    }
}
globalSearch(window, 'StackOverflow');

I bet most browsers will hit a warning for too much looping.

Respondido 24 ago 12, 03:08

Notice that your funtion get stuck using window como tu obj. - Ravan Scafi

@Ravan: check out my answer, which accounts for that. - JCOC611

This code, based on the other answer, allows for all possible value matches to be found.

function globalSearch(startObject, value, returnFirstResult = false) {
    var stack = [[startObject,'']];
    var searched = [];
    var found = new Set();

    var isArray = function(test) {
        return Object.prototype.toString.call( test ) === '[object Array]';
    }

    while(stack.length) {
        var fromStack = stack.pop();
        var obj = fromStack[0];
        var address = fromStack[1];

        if( typeof obj == typeof value && obj == value) {
            if (returnFirstResult) {
                return address == '' ? false : address;
            }
            found.add(address)
        }if(typeof obj == "object" && searched.indexOf(obj) == -1){
           if ( isArray(obj) ) {
                var prefix = '[';
                var postfix = ']';
           }else {
                var prefix = '.';
                var postfix = '';
           }
           for( i in obj ) {
                stack.push( [ obj[i], address + prefix + i + postfix ] );
           }
           searched.push(obj);
        }
    }
    return Array.from(found);
}

Respondido el 06 de junio de 19 a las 03:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.