I have a problem with this structure:
const ob = {
name: ''
ob: {}
arr: []
}
I want to check if all values, are empty.
If I have only strings and arrays, the problem is trivial, but with an object my best solution is something like that,
const test = Object.values(ob).reduce((acc, curr) => {
const isPlainObject = typeof curr === 'object' && !Array.isArray(curr);
if (isPlainObject) !Object.values(curr).length ? (acc = false) : null;
else !curr.length ? (acc = false) : null;
return acc;
}, true);
I'm not satisfied with this, did anybody face similar problem and can help me with that?
You could check the various types and then either the length or the count of (own enumerable) keys or the value.
const
isEmpty = object => Object.values(object).every(v => {
if (Array.isArray(v)) return v.length === 0;
if (v && typeof v === 'object') return Object.keys(v).length === 0;
return v === '';
}),
ob = { name: '', ob: {}, arr: [] };
console.log(isEmpty(ob));
Maybe not so much better than your solution, C233. But I'll give it a shot ;)
const isEmpty = (val) => {
if(!val) {
return true;
}
if (typeof val === 'object') {
return Object.values(val).length === 0;
}
if (typeof val === 'string') {
return val.length === 0;
}
return false;
}
const hasEmptyProps = (obj) => Object.values.map(isEmpty).reduce((result, curr) => result && curr, true);
Related
I created method what needs to return proper length of items:
#Input() items: any[];
calculateCount(type?: string | string[]): number {
if (typeof type === 'object') {
for (let value of type) {
this.items && this.items.filter((item) => item.type === value).length;
}
//TODO: return sum of values from loop;
}
if (typeof type === 'string') {
return this.items && this.items.filter((item) => item.type === value).length;
}
return this.items && this.items.length;
}
But I need suggestions how to return sum of values from loop. Thanks
let total = loopitems.reduce((acc, val) => acc += val.itemToSumUp, 0);
loopitems being all the items needed to be counted.
itemToSumUp is the property wich you need to sum (don't use .xxx if you need the entire object.)
0 = start count from zero.
I was looking for the solution and found one by using .some() method instead of for loop calculation.
Here what I've done:
calculateCount(type?: string | string[]): number {
if (Array.isArray(type)) {
return this.items && this.items.filter((item) => type.some((someType) => someType === item.type)).length;
}
if (typeof type === 'string') {
return this.items && this.items.filter((item) => item.type === value).length;
}
return this.items && this.items.length;
}
I hope this should be helpful.
I wanted to find out if my object is empty or not for all its nested objects and key-value pairs.
for e.g.,
const x = {
a:"",
b:[],
c:{
x:[]
},
d:{
x:{
y:{
z:""
}
}
}
};
this should be an empty object and if any of this contains single value then it should be non empty.
Here is the way to do what using recursion
const x = {
a:"",
b:[],
c:{
x:[]
},
d:{
x:{
y:{
z:''
}
}
}
};
function checkEmpty(obj){
for(let key in obj){
//if the value is 'object'
if(obj[key] instanceof Object === true){
if(checkEmpty(obj[key]) === false) return false;
}
//if value is string/number
else{
//if array or string have length is not 0.
if(obj[key].length !== 0) return false;
}
}
return true;
}
console.log(checkEmpty(x))
x.d.x.y.z = 0;
console.log(checkEmpty(x));
You can write a recursive function like following. Function creates a set with 2 possible values true and false. If the size of set is 1 and the value being false, which mean that the object is empty.
const x = {a:"",b:[],c:{x:[]},d:{x:{y:{z:""}}}};
function isEmpty(o, r = new Set()) {
for (let k in o) {
if(typeof o[k] === "object") {
if(Array.isArray(o[k])) r.add(!!o[k].length);
else isEmpty(o[k],r);
} else r.add(!(o[k] === "" || o[k] === undefined || o[k] === null));
}
return r;
}
let result = isEmpty(x);
console.log(result.has(false) && result.size == 1);
I will use a recursive approach for this one, we iterate over the object.keys() and check is every value related to the key is empty, in the case the value is an object, we go one level deeper to check it.
const x = {
a:"",
b:[],
c:{x:[]},
d:{x:{y:{z:""}}}
};
const x1 = [0,0,0];
const x2 = {0:0,1:0,2:0};
const isEmpty = (obj, empty=true) =>
{
Object.keys(obj).forEach((key) =>
{
if (typeof obj[key] === "object")
empty = isEmpty(obj[key], empty);
else
empty = empty && (obj[key].length === 0);
// Return early if we detect empty here.
if (!empty) return empty;
});
return empty;
}
console.log("original x: ", isEmpty(x));
x.a = "I'm not empty";
console.log("x after edit: ", isEmpty(x));
console.log("x1: ", isEmpty(x1));
console.log("x2: ", isEmpty(x2));
try (we use here recursion, fat arrow, obj. keys, reduce, ternary operator and object checking)
let isEmpty = o => o.constructor.name === "Object" ?
Object.keys(o).reduce((y,z)=> y&&isEmpty(o[z]) ,true) : o.length == 0;
const x = {
a:"",
b:[],
c:{
x:[]
},
d:{
x:{
y:{
z:""
}
}
}
};
let isEmpty = o => o.constructor.name === "Object" ?
Object.keys(o).reduce((y,z)=> y&&isEmpty(o[z]) ,true) : o.length == 0;
// Test
console.log(isEmpty(x));
x.d.x.y.z="Smile to life and life will smile to you";
console.log(isEmpty(x));
I want use extend a javascript object, upto all possible nested levels.
I am using this function (the returned function) as iterator
var ObjWalk = (function( GLOBAL_APP_CONFIG,GLOBAL_METHODS){
if (typeof GLOBAL_APP_CONFIG !== 'object' || GLOBAL_APP_CONFIG === null) GLOBAL_APP_CONFIG = {};
const maxobjdepth = GLOBAL_APP_CONFIG.maxobjdepth || 99;
const endvar = GLOBAL_APP_CONFIG.walkendkey || '$W_END';
let ifEndForObjWalk = GLOBAL_METHODS && GLOBAL_METHODS.ifEndForObjWalk;
if(typeof ifEndForObjWalk !== 'function') {
ifEndForObjWalk = function(obj, depth) {
return ((depth < maxobjdepth && typeof obj === 'object'
&& obj !== null && obj[endvar] !== true) ? obj : false);
};
};
const walkInto = function(fun, rt, obj, key, depth, isLast) {
if(!depth) depth = 0;
fun(obj, key, rt, depth || 0, typeof isLast === 'boolean' ? isLast : true);
const ob = ifEndForObjWalk(obj, depth);
if (ob) {
const kys = Object.keys(ob);
const lastln = kys.length - 1;
const deep = depth + 1;
for (let z = 0; z <= lastln; z += 1) {
walkInto(fun, ob, ob[kys[z]], kys[z], deep, (z === lastln));
}
}
};
return walkInto;
})();
The iterators simply iterates each and every possible values and call that value via a function, that is passed at calling.
I know there may be some other solutions available for deep extending javascript object, but i want to achieve deep extend via using this iterator only.
This is what i have done
const deepExtend = function(obj1, obj2) {
let ptr1 = obj1;
// resolve if new value should be array or object or no change
function resolve(ab, vl){
if ((typeof vl === 'object')
&& (ab === undefined || typeof ab !== 'object'
|| (vl === null && ab !== null)
|| (vl !== null && ab === null)
|| (Array.isArray(vl) && !Array.isArray(ab))
|| (!Array.isArray(vl) && Array.isArray(ab))
|| (vl.length > ab.length)
)
) {
ab = vl === null ? vl : (Array.isArray(vl) ? new Array(vl.length) : {});
}
return ab;
}
ptr1 = resolve(obj1, obj2);
if (typeof ptr1 !== 'object') return ptr1;
// ptrs1 = Array of pointers that will make the changes to a particular depth at a time.
const ptrs1 = [[null,true],[ptr1,true]];
// below vl = value, ky = key, ob = someOb where someOb[ky] = vl
// dpt = nested depth
// isLast = if the `ky` is the last key of someOb
ObjWalk(function(vl, ky, ob, dpt, isLast){
if (ky && dpt) {
// whenever a object found
if (typeof vl === 'object') {
ptr1[ky] = resolve(ptr1[ky], vl);
if(ptr1[ky] !== null) {
if (ptrs1[dpt+1]) {
// update if pointer already exists
ptrs1[dpt+1] = [ptr1[ky], isLast];
} else {
// or push a new pointer
ptrs1.push([ptr1[ky], isLast]);
}
ptr1 = ptr1[ky];
}
} else {
ptr1[ky] = vl;
if (isLast) {
let ind = dpt;
let cr = ptrs1[dpt];
// find the closest sub pointer that should change next
do {
ind--;
cr = ptrs1[ind];
ptr1 = cr[0];
} while (cr[1] !== true && ind);
}
}
}
}, null, obj2);
return obj1;
}
Sandbox for the one failure is here
https://jsfiddle.net/rfn3ejy0/5/
Its difficult to debug where is the issue.
Any help there?
This question already has answers here:
Remove empty elements from an array in Javascript
(49 answers)
Closed 5 years ago.
I'm having an issue with this function that recursively removes empty values from an object:
const _ = require('lodash')
function sanitize(object) {
Object.entries(object).forEach(([key, val]) => {
if (
val == null ||
Number.isNaN(val) ||
(typeof val === 'string' && isOnlyWhitespace(val)) ||
(typeof val === 'object' && Object.keys(sanitize(val)).length === 0)
) {
delete object[key]
}
});
// Remove `undefined` values leftover from using `delete` on an array.
if (Array.isArray(object)) {
_.pull(object, undefined); // THIS IS THE LINE IM TRYING TO CHANGE
}
return object;
}
function isOnlyWhitespace(str) {
return !(/\S/).test(str.trim());
}
I'm trying to replace _.pull(object, undefined) with vanilla JS, but nothing seems to give the right output (I've tried using stuff like filter.)
Here is a snippet you can run to see both outputs:
// LODASH VERSION
function lodashSanitize(object) {
Object.entries(object).forEach(([key, val]) => {
if (
val == null ||
Number.isNaN(val) ||
(typeof val === 'string' && isOnlyWhitespace(val)) ||
(typeof val === 'object' && Object.keys(lodashSanitize(val)).length === 0)
) {
delete object[key]
}
});
// Remove `undefined` values leftover from using `delete` on an array.
if (Array.isArray(object)) {
_.pull(object, undefined); // THIS IS THE LINE IM TRYING TO CHANGE
}
return object;
}
// MY VERSION
function mySanitize(object) {
Object.entries(object).forEach(([key, val]) => {
if (
val == null ||
Number.isNaN(val) ||
(typeof val === 'string' && isOnlyWhitespace(val)) ||
(typeof val === 'object' && Object.keys(mySanitize(val)).length === 0)
) {
delete object[key]
}
});
// Remove `undefined` values leftover from using `delete` on an array.
if (Array.isArray(object)) {
object = object.filter(val => val != null) // THIS IS MY ATTEMPT
}
return object;
}
function isOnlyWhitespace(str) {
return !(/\S/).test(str.trim());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<button id="lodash">Show lodash output</button>
<button id="me">Show my output</button>
<p id="output" />
<script>
/**
* Fiddle-related code, you can ignore this
*/
const lodashBtn = document.querySelector('#lodash')
const meBtn = document.querySelector('#me')
const output = document.querySelector('#output')
function createExampleInput() {
const input = {
name: 'John',
grades: [
90,
undefined,
50,
null
]
};
return input;
}
lodashBtn.addEventListener('click', () => {
output.textContent = JSON.stringify(lodashSanitize(createExampleInput()), null, 4)
});
meBtn.addEventListener('click', () => {
output.textContent = JSON.stringify(mySanitize(createExampleInput()), null, 4)
});
</script>
The problem is that filter returns a new array. Why not just use a for loop and splice:
if (Array.isArray(object)) {
for (var i = object.length - 1; i >= 0; i--) {
if (object[i] === undefined) {
object.splice(i, 1);
}
}
}
I have an object like
{ "status": "success", "auth": { "code": "23123213", "name": "qwerty asdfgh" } }
I want to convert it to dot notation (one level) version like:
{ "status": "success", "auth.code": "23123213", "auth.name": "qwerty asdfgh" }
Currently I am converting the object by hand using fields but I think there should be a better and more generic way to do this. Is there any?
Note: There are some examples showing the opposite way, but i couldn't find the exact method.
Note 2: I want it for to use with my serverside controller action binding.
You can recursively add the properties to a new object, and then convert to JSON:
var res = {};
(function recurse(obj, current) {
for(var key in obj) {
var value = obj[key];
var newKey = (current ? current + "." + key : key); // joined key with dot
if(value && typeof value === "object") {
recurse(value, newKey); // it's a nested object, so do it again
} else {
res[newKey] = value; // it's not an object, so set the property
}
}
})(obj);
var result = JSON.stringify(res); // convert result to JSON
Here is a fix/hack for when you get undefined for the first prefix. (I did)
var dotize = dotize || {};
dotize.parse = function(jsonobj, prefix) {
var newobj = {};
function recurse(o, p) {
for (var f in o)
{
var pre = (p === undefined ? '' : p + ".");
if (o[f] && typeof o[f] === "object"){
newobj = recurse(o[f], pre + f);
} else {
newobj[pre + f] = o[f];
}
}
return newobj;
}
return recurse(jsonobj, prefix);
};
You can use the NPM dot-object (Github) for transform to object to dot notation and vice-versa.
var dot = require('dot-object');
var obj = {
id: 'my-id',
nes: { ted: { value: true } },
other: { nested: { stuff: 5 } },
some: { array: ['A', 'B'] }
};
var tgt = dot.dot(obj);
Produces
{
"id": "my-id",
"nes.ted.value": true,
"other.nested.stuff": 5,
"some.array[0]": "A",
"some.array[1]": "B"
}
const sourceObj = { "status": "success", "auth": { "code": "23123213", "name": "qwerty asdfgh" } }
;
const { auth, ...newObj } = sourceObj;
const resultObj = {
...newObj,
..._.mapKeys(auth, (val, key) => `auth.${key}`)
}
// function approach
const dotizeField = (obj, key) => {
const { ...newObj } = sourceObj;
delete newObj[key];
return {
...newObj,
..._.mapKeys(obj[key], (val, subKey) => `${key}.${subKey}`)
}
}
const resultObj2 = dotizeField(sourceObj, 'auth');
console.log(sourceObj, resultObj, resultObj2);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
i have done some fix:
export function toDotNotation(obj,res={}, current='') {
for(const key in obj) {
let value = obj[key];
let newKey = (current ? current + "." + key : key); // joined key with dot
if(value && typeof value === "object") {
toDotNotation(value,res, newKey); // it's a nested object, so do it again
} else {
res[newKey] = value; // it's not an object, so set the property
}
}
return res;
}
I wrote another function with prefix feature. I couldn't run your code but I got the answer.
Thanks
https://github.com/vardars/dotize
var dotize = dotize || {};
dotize.convert = function(jsonobj, prefix) {
var newobj = {};
function recurse(o, p, isArrayItem) {
for (var f in o) {
if (o[f] && typeof o[f] === "object") {
if (Array.isArray(o[f]))
newobj = recurse(o[f], (p ? p + "." : "") + f, true); // array
else {
if (isArrayItem)
newobj = recurse(o[f], (p ? p : "") + "[" + f + "]"); // array item object
else
newobj = recurse(o[f], (p ? p + "." : "") + f); // object
}
} else {
if (isArrayItem)
newobj[p + "[" + f + "]"] = o[f]; // array item primitive
else
newobj[p + "." + f] = o[f]; // primitive
}
}
return newobj;
}
return recurse(jsonobj, prefix);
};
Following what #pimvdb did (a compact and effective solution he submitted), I added a little modification that allows me have a function that can be easily exported:
function changeObjectToDotNotationFormat(inputObject, current, prefinalObject) {
const result = prefinalObject ? prefinalObject : {}; // This allows us to use the most recent result object in the recursive call
for (let key in inputObject) {
let value = inputObject[key];
let newKey = current ? `${current}.${key}` : key;
if (value && typeof value === "object") {
changeObjectToDotNotationFormat(value, newKey, result);
} else {
result[newKey] = value;
}
}
return result;
}
i think this would be more elegant...
const toDotNot = (input, parentKey) => Object.keys(input || {}).reduce((acc, key) => {
const value = input[key];
const outputKey = parentKey ? `${parentKey}.${key}` : `${key}`;
// NOTE: remove `&& (!Array.isArray(value) || value.length)` to exclude empty arrays from the output
if (value && typeof value === 'object' && (!Array.isArray(value) || value.length)) return ({ ...acc, ...toDotNot(value, outputKey) });
return ({ ...acc, [outputKey]: value });
}, {});
const input = {a: {b: 'c', e: {f: ['g', null, {g: 'h'}]}}, d: []};
const output = toDotNot(input);
console.log(output);
results in:
// output:
{
"a.b": "c",
"a.e.f.0": "g",
"a.e.f.1": null,
"a.e.f.2.g": "h",
"d": []
}
There are already lots of answers here, but for Typescript this solution works pretty well for me and is typed:
type EncapsulatedStringObject = Record<string, string | object>;
export function convertKeysToDotNotation( object: EncapsulatedStringObject, prefix: string = '' ): Record<string, string> {
const result: Record<string, string> = {};
Object.keys( object ).forEach( key => {
const newPrefix = prefix ? `${prefix}.${key}` : key;
const value = object[ key ];
if ( typeof value === 'object' ) {
Object.assign( result, convertKeysToDotNotation( object[ key ] as EncapsulatedStringObject, newPrefix ) );
} else {
result[ newPrefix ] = value;
}
} );
return result;
}