Dig till deepest .value or .content - javascript

I am trying to write a function that digs an object till it gets to the last .value or .content property. I wrote this and for the life of me I cant figure out why it breaks.
var jscGetDeepest = function(obj) {
try {
console.info(Math.round(Math.random() * 10) + ' starting jscGetDeepest:', obj, obj.toString());
} catch(ignore) {}
while (obj && ('contents' in obj || 'value' in obj)) {
if ('contents' in obj) {
obj = obj.contents;
} else if ('value' in obj) {
obj = obj.value;
}
//console.info('loop jscGetDeepest:', obj.toString());
}
if (obj || obj === 0) {
obj = obj.toString();
}
console.info('finaled jscGetDeepest:', obj);
return obj;
}

The issue occurs when inner value in the next iteration is not an object. In this case you get an error message, because in operand can't be used with primitives.
To fix it check for object before trying to get deeper. Here is a fixed an slightly improved version with JSON.stringify instead of toString (maybe better to return object itself without stringifying it?):
var jscGetDeepest = function (obj) {
while (typeof obj === 'object' && obj !== null && ('contents' in obj || 'value' in obj)) {
if ('contents' in obj) {
obj = obj.contents;
} else if ('value' in obj) {
obj = obj.value;
}
}
if (typeof obj === 'object') {
obj = JSON.stringify(obj);
}
return obj;
}
alert( jscGetDeepest({value: {name: 2, contents: {name: 3, value: 23}}}) );
alert( jscGetDeepest({value: {name: 2, value: {name: 3, contents: {name: 4}}}}) );

Related

How to MAP an Object Deeply

I want to change somthing in my all values of object let say for example
var a = {a:{c:1},b:2};
desire output : a = {a:{c:5},b:10} //multiply by 5
I am doing like this
var m = function n (o){
return Object.keys(o).map(function(v,i){
if(o[v] !== null && typeof o[v] === 'object') return n(o[v])
else return (o[v]*5);
})
}
a = m({a:{c:1},b:2})
But getting output of a
[
[
5
],
10
]
To generate one object from another, people will frequently use reduce:
var a = {a: {c: 1}, b: 2};
var m = function n(o) {
return Object.keys(o).reduce(function(newObj, key) {
var value = o[key];
if (value !== null && typeof value === 'object') {
newObj[key] = n(value);
} else {
newObj[key] = value * 5;
}
return newObj;
}, {});
};
console.log(m(a));
We pass in a new object as the "accumulator" and set properties on it, returning it from the reduce callback each time so it passes through the entire process.
This is sometimes called an "abusage" of reduce, since the value of the accumulator never changes (it's always the same object; the object's state changes, but not the object), whereas normally the reduce callback alters the value passing through it.
If you prefer, forEach closing over the new object is the other common way:
var a = {a: {c: 1}, b: 2};
var m = function n(o) {
var newObj = {};
Object.keys(o).forEach(function(key) {
var value = o[key];
if (value !== null && typeof value === 'object') {
newObj[key] = n(value);
} else {
newObj[key] = value * 5;
}
});
return newObj;
};
console.log(m(a));
You can do it recursively:
var myObj = {a:{c:1},b:2};
function changeChild(obj) {
var keys = Object.keys(obj);
keys.forEach(key => {
if (typeof obj[key] === "object") {
changeChild(obj[key])
} else if (typeof obj[key] === "number") {
obj[key] = obj[key] * 5;
}
});
}
changeChild(myObj);
console.log(myObj);
A cleaner way...
var m = function (object) {
for(var key in object) {
if(!!object[key] && typeof object[key] === 'object') {
object[key] = m(object[key]);
} else {
object[key] *= 5;
}
}
return object;
};
Array.map function returns Array..this is the reason...and if you know what to change inside the object, why won't you do it directly?
From my point of view you're doing redundant executions for something really simple.
you're creating an anonymous function, using Object.keys to get a new array (after it iterated your object)
you're using Array.map to create another array from this array and inside it creating (another) anonymous function to iterate the new array, and on every iteration you're creating another 4 conditional branches..

All object property is not empty javascript

How to check the following object, make sure every property is not equal to undefined, null or empty string?
userObj = {
name: {
first: req.body.first,
last: req.body.last
},
location: {
city: req.body.city
},
phone: req.body.phone
}
I can check req.body one by one like if(req.body.first) but that's too tedious if I have many params.
You can simply use Array.prototype.some() method over Object.values() to implement this in a recursive way:
function isThereAnUndefinedValue(obj) {
return Object.values(obj).some(function(v) {
if (typeof v === 'object'){
if(v.length && v.length>0){
return v.some(function(el){
return (typeof el === "object") ? isThereAnUndefinedValue(el) : (el!==0 && el!==false) ? !el : false;
});
}
return isThereAnUndefinedValue(v);
}else {
console.log(v);
return (v!==0 && v!==false) ? !v : false;
}
});
}
In the following function we will:
Loop over our object values.
Check if the iterated value is an object we call the function recursively with this value.
Otherwise we will just check if this is a truthy value or not.
Demo:
This is a working Demo:
userObj = {
name: {
first: "req.body.first",
last: [5, 10, 0, 40]
},
location: {
city: "req.body.city"
},
phone: "req.body.phone"
}
function isThereAnUndefinedValue(obj) {
return Object.values(obj).some(function(v) {
if (typeof v === 'object'){
if(v.length && v.length>0){
return v.some(function(el){
return (typeof el === "object") ? isThereAnUndefinedValue(el) : (el!==0 && el!==false) ? !el : false;
});
}
return isThereAnUndefinedValue(v);
}else {
console.log(v);
return (v!==0 && v!==false) ? !v : false;
}
});
}
console.log(isThereAnUndefinedValue(userObj));
You will get a function that validates every object and its sub objects in a recursive way.
To check for truthy values (values other than false, '', 0, null, undefined):
const hasOnlyTruthyValues = obj => Object.values(obj).every(Boolean);
Example:
const hasOnlyTruthyValues = obj => Object.values(obj).every(Boolean);
const object = {
a: '12',
b: 12,
c: ''
};
console.log(hasOnlyTruthyValues(object));
The example you posted (if (res.body.first) ...) also checks for a truthy value. If you want to allow false and 0 (which are falsy, but you didn't mention them in your question), use:
const hasOnlyAllowedValues = obj => Object.values(obj).every(v => v || v === 0 || v === false);
you can create a simple validator for yourself like function below
var body = {
a: 1,
b: 3
};
var keys = ['a', 'b', 'c'];
function validate(body, keys) {
for (var i in keys) {
if (!body[keys[i]]) {
return false;
}
}
return true;
}
console.log(validate(body, keys));
you can use the following validator to check, it works for a nested object as well
function objectValidator(obj){
let ret = true;
for(property in obj){
//console.log(property, obj[property], typeof obj[property]);
if(typeof obj[property] == "object"){
if(obj[property] instanceof Array){
ret = (ret & true);
}
else if(obj[property] == null){
ret = false;
}
else{
ret = (ret & objectValidator(obj[property]));
}
}
else if(typeof obj[property] == "string"){
if(obj[property] == ""){
ret = false;
}
}
else if(typeof obj[property] == "undefined"){
ret = false;
}
}
return ret;
}
let a = {
b : 1,
c :{
d: 2,
e: [3, 4],
f : ""
}
}
console.log(objectValidator(a));
In your post you use:
if(req.body.first)
I don't know if you are checking req and body before hand, but this should be:
if ( typeof req == "object" && typeof req.body == "object"
&& req.body.first ) {
That is a safer way to check before use.
Or, if you want to embed in the object:
function fnTest(a,b,c) {
return (typeof a == "object" && typeof b == "object" && c) ? c : "";
};
userObj = {name:{
first: fnTest(req, req.body, req.body.first)
,last: fnTest(req,req.body, req.body.last)
},location:{
city: fnTest(req,req.body,req.body.city)
},phone: fnTest(req.body.phone)
};

recursively remove undefined from object (including parent)

I'm trying to find a solution to a problem where I need to remove undefined from nested object including all parents if there are no values there, please consider example:
var test = {
foo : {
bar : {
baz : undefined
}
},
bar : 1
}
So my task is to remove baz along with bar and foo but still have bar at the root level;
I know that it's trivial task to solve with 2 for loops, I'm just wondering if there are more elegant and clean solutions which will use recursive stack instead?
Thanks in advance!
Depth-first recursion should be able to handle it:
function cleanse(obj, path) {
Object.keys(obj).forEach(function(key) {
// Get this value and its type
var value = obj[key];
var type = typeof value;
if (type === "object") {
// Recurse...
cleanse(value);
// ...and remove if now "empty" (NOTE: insert your definition of "empty" here)
if (!Object.keys(value).length) {
delete obj[key]
}
}
else if (type === "undefined") {
// Undefined, remove it
delete obj[key];
}
});
}
Example:
var test = {
foo : {
bar : {
baz : undefined
}
},
bar : 1
};
cleanse(test);
function cleanse(obj, path) {
Object.keys(obj).forEach(function(key) {
// Get this value and its type
var value = obj[key];
var type = typeof value;
if (type === "object") {
// Recurse...
cleanse(value);
// ...and remove if now "empty" (NOTE: insert your definition of "empty" here)
if (!Object.keys(value).length) {
delete obj[key]
}
}
else if (type === "undefined") {
// Undefined, remove it
delete obj[key];
}
});
}
console.log(test);
Note that that only visits own, enumerable properties of the objects whose names are not Symbols (ES2015+). If you also want to handle properties inherited from prototypes, or non-enumerable properties, or properties whose names are Symbols, you'll need to adjust to handle that. (You can get non-enumerable properties on an ES5 or later JavaScript engine via getOwnPropertyNames.)
Below example can help you get started.
Without delete keys with empty values:
var test = {
foo: {
bar: {
baz: undefined,
bar: {
baz: undefined
}
}
},
bar: 1,
baz: undefined
}
function loop(obj) {
var t = obj;
for (var v in t) {
if (typeof t[v] == "object")
loop(t[v]);
else if (t[v] == undefined)
delete t[v];
}
return t;
}
var output = loop(test);
console.log(output);
Deleting keys with empty values:
var test = {
foo: {
bar: {
baz: undefined,
bar: {
baz: undefined
}
}
},
bar: 1,
baz: undefined
}
function loop(obj) {
var t = obj;
for (var v in t) {
if (typeof t[v] == "object")
if (!t[v].length)
delete t[v];
else
loop(t[v]);
else if (t[v] == undefined)
delete t[v];
}
return t;
}
var output = loop(test);
console.log(output);
IMO this is much cleaner but probably a smidge slower
const cleanUndefined = object => JSON.parse(JSON.stringify(object));
const testWithoutUndefined = cleanUndefined(test)
I took the function proposed by #T.J. Crowder and changed it to use for ... of and Object.entries(obj). I also return the object for convenience.
function cleanseObject(obj) {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object') {
cleanseObject(value)
if (!Object.keys(value).length) delete obj[key]
} else if (typeof value === 'undefined') {
delete obj[key]
}
}
return obj
}
function cleanPayload(obj: any) {
Object.keys(obj).forEach(key => {
const value = obj[key];
const type = typeof value;
if (!value || !type) {
delete obj[key];
} else if (type === 'object') {
cleanPayload(value);
if (!Object.keys(value).length) {
if (key != 'attributes') {
delete obj[key];
}
}
}
});
return obj;
}
Without mutating the original object
const cleanse = obj => {
const newObj = Array.isArray(obj) ? [...obj] : { ...obj };
Object.keys(newObj).forEach((key) => {
// Get this value and its type
const value = newObj[key];
var type = typeof value;
if (type === "object") {
// Recurse...
newObj[key] = cleanse(value);
// ...and remove if now "empty" (NOTE: insert your definition of "empty" here)
if (!Object.keys(value).length) {
delete newObj[key]
}
}
else if (type === "undefined") {
// Undefined, remove it
delete newObj[key];
}
});
return newObj;
};
console.log(
cleanse({ a: { b: undefined, c: 22 }}),
cleanse({ a: [{ b: undefined, c: 22 }] }),
);
Here is the code, which will also remove that undefined contained key and empty object from the main object.
var test = {
foo: {
bar: {
baz: undefined,
bar: {
baz: undefined,
}
}
},
bar: 1,
baz: undefined
}
function loop(obj) {
var t = obj;
for (var v in t) {
if (typeof t[v] == "object"){
loop(t[v]);
if(!Object.keys(t[v]).length){
delete t[v];
}
} else if (t[v] == undefined){
delete t[v];
}
}
return t;
}
var output = loop(test);
console.log(output);

How can I filter an object of all properties that are objects?

I'm trying to make a copy of an object that only includes the properties that are not objects. But the child objects get copied along with it.
var testObject = {
stringProperty: "hi",
intProperty: 4,
objectProperty: {},
nullProperty: null
};
console.log(removeChildObjects(testObject));
function removeChildObjects(object) {
var keys = Object.keys(object);
var newObject = {};
keys.forEach(function(key) {
console.log(key, object[key], typeof object[key]);
if (typeof object[key] != "object") {
newObject[key] = object[key];
}
});
return object;
}
Also check it out here https://jsfiddle.net/uss94sc3/1/
If you want to strictly filter out object properties (keeping null and undefined properties), then you cannot rely on the broken typeof unary operator.
typeof null
// "object"
You can either change your code to:
function removeChildObjects(object) {
var keys = Object.keys(object);
var newObject = {};
keys.forEach(function(key) {
if (typeof object[key] != "object" || object[key] == null) {
newObject[key] = object[key];
}
});
return newObject;
}
or more succinctly with underscore:
function removeChildObjects(object) {
return _.omit(object, _.isObject);
}
You return the same object that passed:
return object;
You should return newObject
return newObject;
Try replacing return object; with return newObject;. It will work a lot better!
https://jsfiddle.net/w3urvpjq/
You may try this
var testObject = {
stringProperty: "hi",
intProperty: 4,
objectProperty: {},
nullProperty: null
};
var filterPrimitive = o => Object.keys(o).reduce((p,k) => {typeof o[k] != "object" && (p[k] = o[k]); return p},{});
document.write("<pre>" + JSON.stringify(filterPrimitive(testObject),null,2) + "</pre>");

finding property in js object by name

I've got many objects that structures aren't the same. But I know that all of them have got property name 'siteName'. My question is how can I get value from this property.
Explame of few objects:
feature1 = {
display: "name",
feature: {
attributes: {
when: '111',
what: '222'
},
geometry: null
infoTemplate: undefined
},
symbol: null
siteName: 'aa'
}
feature2 = {
feature: {
attributes: {
when: '111',
what: '222'
},
geometry: null
infoTemplate: undefined
},
static: {
format: {
weight: 12,
siteName: 'cccc'
},
}
}
Here's a recursive function that should work for you.
It returns the value of the first property found with the name, otherwise returns undefined.
function findByName(obj, prop) {
for (var p in obj) {
if (p === prop) {
return obj[p];
} else if (obj[p] && typeof obj[p] === "object") {
var result = findByName(obj[p], prop);
if (result !== undefined)
return result;
}
}
}
var result = findByName(myObject, "siteName");
Or here's another variation that avoids inherited properties.
function findByName(obj, prop) {
if (obj.hasOwnProperty(prop))
return obj[prop];
for (var p in obj) {
if (obj[p] && typeof obj[p] === "object") {
var result = findByName(obj[p], prop);
if (result !== undefined)
return result;
}
}
}
Recursively loop through the objects:
function find(obj, name) {
for (var k in obj) { // Loop through all properties of the object.
if(k == name){ // If the property is the one you're looking for.
return obj[k]; // Return it.
}else if (typeof obj[k] == "object"){ // Else, if the object at [key] is a object,
var t = find(obj[k], name); // Loop through it.
if(t){ // If the recursive function did return something.
return t; // Return it to the higher recursion iteration, or to the first function call.
}
}
}
}
Usage:
find(feature1, "siteName"); //Returns "aa"
The following function should suit your needs:
function getFirstFoundPropertyValue(searchedKey, object) {
if(typeof object === "object") {
for (var key in object) {
var currentValue = object[key];
if(key === searchedKey) {
return currentValue;
}
var nested = getFirstFoundPropertyValue(searchedKey, currentValue);
if(typeof nested !== "undefined") {
return nested;
}
}
}
}
It returns the value of the key if the key is found, undefined otherwise. If the key appears several times, the first found one will be returned.

Categories

Resources