JavaScript loop through all existing objects to find an object key - javascript

I am trying to debug a system that was built by someone else. I have a minified library that makes an ajax (xhr) call to an external data source and writes the received data to an object or a variable. The call is made immediately after page load so I can't catch it by appending a function to register all XHR call requests. So I am trying to run a loop through all variables and objects that the browser window has. I am trying this:
var foundVar = false;
loopThroughObject(this);
function loopThroughObject(obj) {
if (!foundVar) {
try {
for (var name in obj) {
if (obj[name] && {}.toString.call(obj[name]) === '[object Function]') { //making sure that the object is not a function
} else {
if (name == 'searchedKey') { //found the object with key = searchedKey
console.log(obj[name]);
foundVar = true;
} else {
setTimeout(loopThroughObject.bind(null, obj[name]), 10); //do more recursion of inner objects
}
}
}
} catch (error) {}
}
}
The problem is that the setTimeout(loopThroughObject.bind(null, obj[name]), 0); part stacks up and a memory issue occurs. I tried the simple loopThroughObject(obj[name]), but I am facing the "Too much recursion" error after 25000 loops. It also seems that I am missing something and it causes the loop go through some object types that I don't need. Does anyone know a more efficient way to do that loop?
P.S. I tried a very simple HTML page and the code works fine:
<html>
<head>
<script>
var test = JSON.parse("{\"searchedKey\":\"12\"}");
</script>
</head>
<body>
</body>
</html>

The problem is almost certainly the fact that window has several properties that point back to itself, so your code is going into an infinite loop. There may well be other circular references.
You can remember what objects you've already looked in using a Set, like this (I've also added a few other tweaks; for instance, you can check for a function with typeof x === "function"):
let foundVar = false;
let seen = new Set();
loopThroughObject(this);
function loopThroughObject(obj) {
if (!foundVar) {
try {
for (const name in obj) {
const value = obj[name];
if (typeof value !== "function") {
if (name == "searchedKey") { // found the object with key = searchedKey
console.log(value);
foundVar = true;
break;
} else if (value && typeof value === "object" && !seen.has(value)) {
seen.add(value);
loopThroughObject(value); // No need for setTimeout
}
}
}
} catch (error) {}
}
}

Related

hasOwnProperty() is only checking if a certain property exists in a JSON, but doesn't return anything if it doesn't

I keep trying different methods to check if this JSON contains "attributes." In this way I can determine if the given coordinates are outside of wetlands. If they are in wetlands, "attributes" will exist in the JSON. If they aren't in wetlands, 'attributes' won't be in the JSON.
When I run this function, I am only getting TRUE - when I type in coordinates that are in a wetland (try 43.088 instead, in the JSON url, which returns true).
However I want FALSE for the given url. For some reason when I do console.log("FALSE"), this doesn't appear or return in the console at all if hasOwnProperty('attributes') == false.
Am I missing something?
function(GetData) {
fetch('https://www.fws.gov/wetlandsmapservice/rest/services/Wetlands/MapServer/0/query?where=&text=&objectIds=&time=&geometry=-88.305%2C43.060&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&relationParam=&outFields=WETLAND_TYPE&returnGeometry=false&returnTrueCurves=false&maxAllowableOffset=&geometryPrecision=&outSR=&returnIdsOnly=false&returnCountOnly=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&returnZ=false&returnM=false&gdbVersion=&returnDistinctValues=false&resultOffset=&resultRecordCount=&queryByDistance=&returnExtentsOnly=false&datumTransformation=&parameterValues=&rangeValues=&f=pjson&__ncforminfo=qCOZOO8Kyr4uogGcKvxkzzuK7gmavd4CxwTAkdbAsF2_aT4eeNbB0NpLwCYwiAJSf1ZHqY3CKVZ3osgMevhYGQrqRUQZej5oHaSmnSIaiZZb469Cexv-zqqmgYMuFJAAzrcRxvKXPBz9VnYPnMrM6kBNhO-cz6yK_w5T1mqNu_VXSbjBSihVf4_mlUBSVb9yf4C8scYXWm9Iak2Nfn1dtJACNUHLBHSElLvc1wxFMO2eUWNsD3qpCk3kAcRyYftuFU86n7THyk2IvkIUpxNmDHRxmmbgSYvPLMkl8t41Jzjp_bntkIyOWB0u8cQU2VsfASFUdznRkvrvYrQxgR8eyvsPq5oV_ZoPSksVCew6xev0K_TV2NU-kjojYpowMVXpZtCX9P-Q_7m8ywt2PyLPhEVgQB12ji1S7G5FRzIt6E0SDoXMY1vqQvPtedaNYbBCazXgs05L9DFKdtvrwmQVCeLmpBTduIhF9Sk4kozMnFX6GOANrZJMCI9AssN0DjrhlZkgDVw0l1flF44Zli927CXGTQ-oUpwsn7PPypVkN2iDJf-nz9XNbj82sv1c6B5s5UZVwiOp8VHJfZSDJ8BAYR4z_oONT2JwbVSKKlFKeN72f-Y6EejcB9wPKmn5kYjv7CKkRyIIv4F4cqVWxLK9x33uvEDMTvxX')
.then(function(response) {
return response.json();
})
.then(function(data) {
appendData3(data);
})
.catch(function(err) {
console.log('error: ' + err);
});
function appendData3(data) {
for (let obj of data['features']) {
if (obj.hasOwnProperty('attributes') == false) {
console.log("FALSE");
} else {
console.log("TRUE");
}
}
}
};
The issue is that in the response data['features'] is empty. When iterating over an empty array, nothing within the for...of loop is executed.
const emptyArray = [];
for (const item of emptyArray) {
// body is never executed...
}
If just checking the presence of an item within data['features'] is enough, you could use the length of the array.
function appendData3(data) {
if (data.features.length > 0) {
console.log("TRUE");
} else {
console.log("FALSE");
}
}
To check if one of the elements has the property "attributes" you could use some():
function appendData3(data) {
if (data.features.some(item => item.hasOwnProperty("attributes"))) {
console.log("TRUE");
} else {
console.log("FALSE");
}
}
If you're just trying to find out if a specific point is within one of the wetlands polygons, you could let the server to do the hard job and simplify your request. For example, ask for count.
See returnCountOnly at https://developers.arcgis.com/rest/services-reference/enterprise/query-feature-service-layer-.htm
https://www.fws.gov/wetlandsmapservice/rest/services/Wetlands/MapServer/0/query?geometry=-88.305%2C43.060&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnCountOnly=true&f=pjson
https://www.fws.gov/wetlandsmapservice/rest/services/Wetlands/MapServer/0/query?geometry=-88.305%2C43.088&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnCountOnly=true&f=pjson
I tested your code and this is the problem. When the coordinates are outside the wetlands, the features array is empty, that means nothing happen in your for loop. So do this instead of checking directly inside of your for loop
function appendData3(data) {
// Here we check if features is empty by checking it's length
if (data['features'].length == 0) {
console.log("FALSE")
}
for (let obj of data['features']) {
console.log("TRUE");
}
}
I also see that your for loop is only getting one object every time so instea of doing a for loop, just do it like this:
function appendData3(data) {
var obj = data['features'][0]
if(obj) {
console.log('TRUE')
} else {
console.log('FALSE')
}
}
As you can see, I did it even easier this time, just by getting the first object of features and checking if it exist.
Also, small tip: when you want to check if a condition is false, don't use == false, just put an exclamation mark at the beginning of the if statement. Like that:
if(!obj.hasOwnProperty('attributes')) { 
// Code here will be executed if the condition is false
} else {
// Code here will be executed if the condition is true
}
I hope this help you fixing your problem.
Have a nice day :)

Javascript associative array not working on Jquery objects

I am trying to build a associative array parentTds however it's not working the way I would like.
var parentTds = {};
var index = 0;
$.each(clone, function () {
var $currentItem = $(selectedActivities[index]);
var $currentItemTd = $currentItem.closest('td');
console.log($currentItemTd.get(0));
var borderLeft = 0;
console.log(parentTds[$currentItemTd.get(0)]);
console.log(parentTds[$currentItemTd]);
if (typeof parentTds[$currentItemTd.get(0)] === "undefined") {
console.log('NOT OK');
borderLeft++;
parentTds[$currentItemTd.get(0)] = $currentItemTd;
} else {
console.log('OK');
}
index++;
});
For some reason parentTds[$currentItemTd.get(0)] always returns the 1st item stored. I get NOT OK just the 1st time the loop runs whereas I should be getting NOT OK a few more times. I suspect the problem is parentTds[$currentItemTd.get(0)] itself.
Any ideas please?
Javascript is not as forgiving as for example PHP. When you use this:
if (typeof parentTds[$currentItemTd.get(0)] === "undefined") {
$currentItemTd.get(0) might be evaulated as 0 and therefore the reference is always parentTds[0]
In these circumstance I use to break up the code-block and do something like this:
var cig = $currentItemTd.get(0);
if (typeof parentTds[cig] === "undefined") {

How do I correctly manage multi-level proxying of objects

As demonstrated in this SO question, Proxy objects are a good way to watch for changes in an object.
What if you want to watch changes to subobjects? You'd need to proxy those subobjects as well.
I'm currently working with some code that does that automatically - any time you try to set a property it makes sure that it is wrapped in a proxy. We're doing this so that we can perform an action every time any value changes.
function createProxiedObject(objToProxy) {
return new Proxy(objToProxy, {
set: function (target, key, value) {
//proxy nested objects
if (value !== null && typeof value === 'object') {
value = createProxiedObject(value);
}
target[key.toString()] = value;
handleProxiedObjectChange();
});
This works pretty well, but there is at least one case in which this backfires:
function ensureObjectHasProperty(object, key, default) {
if (object[key] !== null) {
// some validation happens here
return object[key];
} else {
return default;
}
}
...
proxiedObject = somethingThatCreatesAProxiedObject(someValue);
proxiedObject[someProperty] = ensureObjectHasProperty(proxiedObject, someProperty, defaultValue)
The result is that the value under someProperty (which is already being proxied) gets reassigned to the proxied object, causing it to get wrapped in another proxy. This results in the handleProxiedObjectChange method being called more than once each time any part of the object changes.
The simple way to fix it is to never assign anything to the proxied object unless it's new, but as this problem has already happened there's a reasonable chance someone will do it again in the future. How can I fix the set function to not rewrap objects that are already being proxied? Or is there a better way to watch an object so that handleProxiedObjectChange can be called any time the object or any of its subobjects change?
As suggested by #DecentDabbler, using a WeakSet allowed me to ensure I never try to wrap a proxy in another proxy:
const proxiedObjs = new WeakSet();
...
function createProxiedObject(objToProxy) {
// Recursively ensure object is deeply proxied
for (let x in objToProxy) {
subObj = objToProxy[x];
if (subObj !== null && typeof subObj === 'object' && !proxiedObjs.has(subObj)) {
objToProxy[x] = createProxiedObject(subObj);
}
}
let proxied = new Proxy(objToProxy, {
set: function (target, key, value) {
//This check is also new - if nothing actually changes
//I'd rather not call handleProxiedObjectChange
if (_.isEqual(target[key.toString()], value)) {
return true;
}
//proxy nested objects
if (value !== null && typeof value === 'object' && !proxiedObjs.has(value)) {
value = createProxiedObject(value);
}
target[key.toString()] = value;
handleProxiedObjectChange();
});
proxiedObjs.add(proxied);
return proxied;

Javascript function that returns two different types of variables depending on input?

I'm trying to follow the rule and avoid repeating the same code.
I have this single function but depending on the input I want it to either return an array of objects or an object (not an array of just one object)
e.g.(the actual function is much longer and more elaborate than this one obviously, there are just the last few lines after a much longer calculation)
function (nameParameter, ageParameter, inputType)
{
if (inputType === "asObject")
{
var x = {};
x.name = nameParameter;
x.age = ageParameter;
return x;
}
else if (inputType === "asArray")
{
var y = [];
y.push(nameParameter);
y.push(ageParameter);
return y;
}
};
Is this possible and if so is it good practice? Is there some other way around it?
Otherwise I'll have to create two distinct function with almost the exact same code.
Don't do this. Implement one version and add a wrapper function that converts the the other format you may want. That way the caller always gets consistent behaviour, and theres still no code duplication.
function asObject(nameParameter, ageParameter)
{
//Lots of work here.
var x = {};
x.name = nameParameter;
x.age = ageParameter;
return x;
};
function asArray(nameParameter, ageParameter)
{
//Just defer to the other version and repack its response.
var o = asObject(nameParameter, ageParameter);
var y = [o.nameParameter,o.ageParameter ];
return y;
}
You can simplify your code by declaring the object and array with the values already set, but in my opinion if you have this strict type of coding it is not necessary to keep this function... Anyway, here is a simplified version:
function (nameParameter, ageParameter, inputType) {
var ret;
if (inputType === "asObject") {
ret = {
name: nameParameter,
age: ageParameter
};
} else if (inputType === "asArray") {
ret = [nameParameter, ageParameter];
}
return ret;
};
I left it without name and with a semicolon at the end because I guess it has been declared through a variable.
Yes; that will work fine.
Javascript is not strongly-typed; functions can return whatever they want, whenever they want.
if ( typeof inputType == 'object') {
//object part of code
} else {
//array part of code
}

What's the simplest approach to check existence of deeply-nested object property in JavaScript? [duplicate]

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 8 years ago.
I have to check deeply-nested object property such as YAHOO.Foo.Bar.xyz.
The code I'm currently using is
if (YAHOO && YAHOO.Foo && YAHOO.Foo.Bar && YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
}
This works, but looks clumsy.
Is there any better way to check such deeply nested property?
If you expect YAHOO.Foo.Bar to be a valid object, but want to make your code bulletproof just in case it isn't, then it can be cleanest to just put a try catch around it and let one error handler catch any missing segment. Then, you can just use one if condition instead of four that will detect if the terminal property exists and a catch handler to catch things if the intermediate objects don't exist:
try {
if (YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// handle error here
}
or, depending upon how your code works, it might even just be this:
try {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// do whatever you want to do when YAHOO.Foo.Bar.xyz doesn't exist
}
I particularly use these when dealing with foreign input that is supposed to be of a particular format, but invalid input is a possibility that I want to catch and handle myself rather than just letting an exception propagate upwards.
In general, some javascript developers under-use try/catch. I find that I can sometimes replace 5-10 if statements checking input with a single try/catch around a larger function block and make the code a lot simpler and more readable at the same time. Obviously, when this is appropriate depends upon the particular code, but it's definitely worth considering.
FYI, if the usual operation is to not throw an exception with the try/catch, it can be a lot faster than a bunch of if statements too.
If you don't want to use the exception handler, you can create a function to test any arbitrary path for you:
function checkPath(base, path) {
var current = base;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
Example usage:
var a = {b: {c: {d: 5}}};
if (checkPath(a, "b.c.d")) {
// a.b.c.d exists and can be safely accessed
}
var _ = {};
var x = ((YAHOO.Foo || _).Bar || _).xyz;
Consider this utility function:
function defined(ref, strNames) {
var name;
var arrNames = strNames.split('.');
while (name = arrNames.shift()) {
if (!ref.hasOwnProperty(name)) return false;
ref = ref[name];
}
return true;
}
Usage:
if (defined(YAHOO, 'Foo.Bar.xyz')) {
// operate on YAHOO.Foo.Bar.xyz
}
Live demo: http://jsfiddle.net/DWefK/5/
If you need to check the correctness of the path, rather than the existance of the "xyz" member on the "YAHOO.Foo.Bar" object, it will probably be easiest to wrap the call in a try catch:
var xyz;
try {
xyz = YAHOO.Foo.Bar.xyz;
} catch (e) {
// fail;
};
Alternately, you can do some string-kong-fu-magicTM:
function checkExists (key, obj) {
obj = obj || window;
key = key.split(".");
if (typeof obj !== "object") {
return false;
}
while (key.length && (obj = obj[key.shift()]) && typeof obj == "object" && obj !== null) ;
return (!key.length && typeof obj !== "undefined");
}
The use as follows:
if (checkExists("YAHOO.Foo.Bar.xyz")) {
// Woo!
};
This problem is solved quite beautifully by coffeescript (which compiles down to javascript):
if YAHOO.Foo?.Bar?.xyz
// operate on YAHOO.Foo.Bar.xyz
use a try catch.
a={
b:{}
};
//a.b.c.d?true:false; Errors and stops the program.
try{
a.b.c.d;
}
catch(e){
console.log(e);//Log the error
console.log(a.b);//This will run
}
I actually voted to close the question as duplicate of javascript convert dotnotation string into objects.
However, I guess it's a different topic, but the answer there might still be helpful if you don't want to try-catch all the time.

Categories

Resources