I am writing a recursive function for doing a deep comparison of two objects. However, I am getting a syntax error.
this is the function:
function deepEqual(obj1, obj2) {
if (compareArrays(Object.keys(obj1), Object.keys(obj2))){
for (let x of Object.keys(obj1)){
if (typeOf obj1.x == 'object' && typeOf obj2.x == 'object')\
return deepEqual(obj1.x, obj2.x)
else{
if (obj1.x !== obj2.x) return false
}
}
return true
}
else{
return false
}
}
this is the function compareArrays(this has no errors):
function compareArrays(array1, array2) {
if (array2.length === array1.length) {
for (var i = array2.length - 1; i >= 0; i--) {
if (array2[i] !== array1[i]){
return false
}
}
return true
}
else{
return false
}
}
Expected output:
true/false
Actual output:
if (typeOf obj1.x == 'object' && typeOf obj2.x == 'object') return deepEqual(obj1.x, obj2.x)
^^^^
SyntaxError: Unexpected identifier
You mistyped typeof and also \ is useless
Try this
function deepEqual(obj1, obj2) {
if (compareArrays(Object.keys(obj1), Object.keys(obj2))){
for (let x of Object.keys(obj1)){
if (typeof obj1.x == 'object' && typeof obj2.x == 'object')
return deepEqual(obj1.x, obj2.x)
else{
if (obj1.x !== obj2.x) return false
}
}
return true
}
else{
return false
}
}
Related
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 have a program that does a deep compare on 2 objects provided to it and uses recursion to do so. My problem is that since I am using a global variable to retain information, I have to reset it every time prior to making any subsequent calls to the function. Is there someway I can maintain the variable value other than using a global variable and have it be not so cumbersome?
let isEqual = true;
function deepEqual(object1, object2) {
if (!((typeof(object1) == 'object' && typeof(object2) == 'object') || (object1 && object2))) {
return isEqual = object1 === object2;
} else if (typeof(object1) == 'object' && typeof(object2) == 'object') {
if ((object1 && object2)) {
let object1Keys = Object.keys(object1);
let object2Keys = Object.keys(object2);
if (object1Keys.length == object2Keys.length) {
for (let index = 0; index < object1Keys.length; index++) {
if (isEqual) {
if (!(typeof(object1[object1Keys[index]]) == 'object' && typeof(object2[object2Keys[index]]) == 'object')) {
isEqual = (object1[object1Keys[index]] === object2[object2Keys[index]]) && (object1Keys[index] === object2Keys[index]);
} else {
deepEqual(object1[object1Keys[index]], object2[object2Keys[index]]);
}
} else {
return isEqual = false;
}
}
}
}
}
return isEqual;
}
let obj1 = {
a: 'somestring',
b: 42,
c: {
1: 'one',
2: {
4: 'Three'
}
}
};
let obj2 = {
a: 'somestring',
b: 42,
c: {
1: 'one',
2: {
3: 'Three'
}
}
};
console.log("obj1 == obj2 : ");
console.log(deepEqual(obj1, obj2));
let obj3 = {
a: 'somestring',
b: 42,
c: {
1: 'one',
2: {
3: 'Three'
}
}
};
let obj4 = {
a: 'somestring',
b: 42,
c: {
1: 'one',
2: {
3: 'Three'
}
}
};
console.log("obj3 == obj4 : ");
isEqual = true;
console.log(deepEqual(obj3, obj4));
let obj = {name: {gender: "F"}, age: 20};
isEqual = true;
console.log(deepEqual(obj, {name: {gender: "F"}, age: 20}));
You don't need to use it at all: you can do the whole thing via recursion:
function deepEqual(o1, o2){
if (typeof o1 != typeof o2)
return false;
if (typeof o1 != 'object' || o1 === null || o2 === null)
return o1 === o2;
for (var k in o1){
if (!deepEqual(o1[k], o2[k]))
return false;
}
for (var k in o2){
if (!(k in o1))
return false;
}
return true;
}
I have created an utility just to deep compare two object. It uses the recursive call with two object and return true or false.
Github link for repo - https://github.com/maninder-singh/deep-compare
<script src="deep-compare.js"></script>
JS
1. dc(null,null);
2. dc("a","a");
3. dc("a","ab");
4. dc("a",undefined);
5. dc(undefined,undefined);
6. dc({},[]);
7. dc({a:1},{});
8. dc({a:1},{a:1});
9. dc(true,true);
10. dc(true,false);
You can use tested, bullet proof Object equality methods provided my various JS library to perform Object equality testing as illustrated below
lodash Library:
_.isEqual(obj1, obj2)
Or
Custom Tested Method
function deepCompare () {
var i, l, leftChain, rightChain;
function compare2Objects (x, y) {
var p;
// remember that NaN === NaN returns false
// and isNaN(undefined) returns true
if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
return true;
}
// Compare primitives and functions.
// Check if both arguments link to the same object.
// Especially useful on the step where we compare prototypes
if (x === y) {
return true;
}
// Works in case when functions are created in constructor.
// Comparing dates is a common scenario. Another built-ins?
// We can even handle functions passed across iframes
if ((typeof x === 'function' && typeof y === 'function') ||
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)) {
return x.toString() === y.toString();
}
// At last checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) {
return false;
}
if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
return false;
}
if (x.constructor !== y.constructor) {
return false;
}
if (x.prototype !== y.prototype) {
return false;
}
// Check for infinitive linking loops
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
return false;
}
// Quick checking of one object being a subset of another.
// todo: cache the structure of arguments[0] for performance
for (p in y) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
}
else if (typeof y[p] !== typeof x[p]) {
return false;
}
}
for (p in x) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
}
else if (typeof y[p] !== typeof x[p]) {
return false;
}
switch (typeof (x[p])) {
case 'object':
case 'function':
leftChain.push(x);
rightChain.push(y);
if (!compare2Objects (x[p], y[p])) {
return false;
}
leftChain.pop();
rightChain.pop();
break;
default:
if (x[p] !== y[p]) {
return false;
}
break;
}
}
return true;
}
if (arguments.length < 1) {
return true; //Die silently? Don't know how to handle such case, please help...
// throw "Need two or more arguments to compare";
}
for (i = 1, l = arguments.length; i < l; i++) {
leftChain = []; //Todo: this can be cached
rightChain = [];
if (!compare2Objects(arguments[0], arguments[i])) {
return false;
}
}
return true;
}
Reference: Object comparison in JavaScript
function deepEqual(object1, object2) {
//check if the given objects have the same datatype
if (typeof(object1) === typeof(object2)) {
//check if the given object is a primitive datatype and how to handle null values
if ((typeof(object1) !== 'object') && (typeof(object2) !== 'object') ||
object1 === null || object2 === null) {
return object1 === object2;
} else {
//if they are both objects
if (object1 !== null && object2 !== null) {
let object1Keys = Object.keys(object1);
let object2Keys = Object.keys(object2);
//check if the arrays have the same length
if (object1Keys.length === object2Keys.length) {
let isEqual;
for (let index = 0; index < object1Keys.length; index++) {
//make sure both key:value pairs match
if (object1Keys[index] === object2Keys[index]) {
//check if the current value is another object
if (typeof(object1[object1Keys[index]]) === 'object' &&
typeof(object2[object2Keys[index]]) === 'object') {
return deepEqual(object1[object1Keys[index]], object2[object2Keys[index]]);
} else {
isEqual = (object1[object1Keys[index]] === object2[object2Keys[index]]);
}
} else {
return false; //return false if keys dont match
}
}
return isEqual;
} else {
return false; //return false if 2 arrays dont have the same length
}
}
}
} else {
return false; //return false if 2 object types dont match
}
}
let obj1 = {
a: 'somestring',
b: 42,
c: {
1: 'one',
2: {
3: 'Three'
}
}
};
let obj2 = {
a: 'somestring',
b: 42,
e: {
1: 'one',
2: {
3: 'Three'
}
}
};
console.log("obj1 == obj2 : ");
console.log(deepEqual(obj1, obj2));
let obj3 = {
a: 'somestring',
b: 42,
c: {
1: 'one',
2: {
4: 'Three'
}
}
};
let obj4 = {
a: 'somestring',
b: 42,
c: {
1: 'one',
2: {
3: 'Three'
}
}
};
console.log("obj3 == obj4 : ");
console.log(deepEqual(obj3, obj4));
let obj = {
name: {
gender: "F"
},
age: 20
};
console.log(deepEqual(obj, {
name: {
gender: "F"
},
age: 20
}));
console.log('null == obj3');
console.log(deepEqual(null, obj3));
console.log('5 == obj3');
console.log(deepEqual(5, obj3));
console.log('null == null');
console.log(deepEqual(null, null));
console.log('10 == 5');
console.log(deepEqual(10, 5));
console.log(`10 == '10'`);
console.log(deepEqual(10, '10'));
In all honesty, I prefer #Andrew Ridgway's solution. Its very simple and elegant.
However, I did clean-up the function I was using to avoid using global variables.
This is another solution to same problem, though a bit complex.
I am open to further suggestions. Thanks!
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);
}
}
}
So I have a fairly complex object like this:
var obj = {
v1:"ok",
v2:[
{av1:"foo", av2:null}, // notice there's a null here
{av1:"thing", av2:"stuff"}
],
v3: null,
v4:{
ov1:"slim",
ov2:"shady",
ov3:null // null
},
v5:[], // empty
v6:{} // empty
}
I'd like to get this back:
var obj = {
v1:"ok",
v2:[{av1:"foo"},{av1:"thing", av2:"stuff"}],
v4:{ov1:"slim",ov2:"shady"}
}
I'm trying to write a function that can delete anything that is null, undefined, or empty, but it's quickly becoming a spaghetti nightmare, and doesn't work.
I feel like there's a shorter more elegant way to do this, but this is my code so far:
function deleteNulls(o){
for(let i in o){
if(typeof o[i] == "Object"){
o[i] = deleteNulls(o[i])
}
else if(typeof o[i] == "Array"){
o[i] = deleteNulls(o[i])
}
if(o[i] == null || o[i] == "undefined" || o[i] == [] | o[i] == {})
delete o[i]
else {
if(typeof o == "Object"){
delete this.o[i]
return o
}
else if (typeof o == "Array")
return o.filter(k => o[k] != null)
}
}
return o
}
var obj = deleteNulls(obj)
I'm not interested in how to fix errors in the code above. I could get this to work if I wanted to,
I'm wondering if there's an easier way.
I'd suggest using something like lodash. It's tested and peer-reviewed for speed and efficiency.
Something like:
var result = _.omitBy(my_object, _.isNil);
This would remove all null values, you'll need to change the second parameter to remove empty objects and arrays.
It helped me writing this solution to split the logic into a recursive clean function (simplifying existing object structures) and a shouldKeep function (which tells you whether a key can be removed entirely from an object structure based on its value).
Demo Snippet:
var object = {
v1: "ok",
v2: [{
av1: "foo",
av2: null
}, // notice there's a null here
{
av1: "thing",
av2: "stuff"
}
],
v3: null,
v4: {
ov1: "slim",
ov2: "shady",
ov3: null // null
},
v5: [], // empty
v6: {} // empty
}
function shouldKeep (o) {
if (Array.isArray(o)) {
return o.length
} else if (typeof o === 'object') {
return o && Object.keys(o).length
}
return o != null
}
function clean (o) {
if (Array.isArray(o)) {
o.forEach(clean)
var a = o.filter(shouldKeep)
o.length = a.length
for (var i = 0; i < a.length; i++) {
o[i] = a[i]
}
} else if (o && typeof o === 'object') {
Object.keys(o).forEach(function (k) {
clean(o[k])
if (!shouldKeep(o[k])) delete o[k]
})
}
return o
}
console.log(clean(object))
.as-console-wrapper { min-height: 100vh; }
Here is my take at it. Should be good enough for most situations.
var object = {
v1: "ok",
v2: [{
av1: "foo",
av2: null
}, // notice there's a null here
{
av1: "thing",
av2: "stuff"
}
],
v3: null,
v4: {
ov1: "slim",
ov2: "shady",
ov3: null // null
},
v5: [], // empty
v6: {} // empty
}
function isEmpty(v) {
return v == undefined
|| v == null
|| (Array.isArray(v) && v.length === 0)
|| (typeof v === 'object' && Object.keys(v).length === 0)
}
function removeFalsyFromObject(v) {
var keys = Object.keys(v);
keys.forEach(function(k){
var val = v[k];
if (isEmpty(val)) {
delete v[k];
} else if (Array.isArray(val)) {
removeFalsyFromArray(val);
} else if (typeof val === 'object') {
removeFalsyFromObject(val);
}
})
}
function removeFalsyFromArray(a) {
for (var i=0; i<a.length; i++) {
var v = a[i];
if (isEmpty(v)) {
a.splice(i,1);
i--;
} else if (Array.isArray(v)) {
removeFalsyFromArray(v);
} else if (typeof v === 'object') {
removeFalsyFromObject(v);
}
}
}
function clean(obj) {
removeFalsyFromObject(obj);
return obj;
}
console.log(clean(object));
Task:
Give you an obj, it can be 3 types: string, number and number array, Check that they are symmetrical or not, return a Boolean value.
Example:
obj="" return true (Empty string should return true)
obj="1" return true (one char should return true)
obj="11" return true
obj="12" return false
obj="121" return true
obj=1 return true (number<10 should return true)
obj=-1 return false (negative number should return false)
obj=121 return true
**obj=[] return true (Empty array should return true)**
**obj=[1] return true (an array with one element should return true)**
obj=[1,2,3,4,5] return false
**obj=[1,2,3,2,1] return true**
**obj=[11,12,13,12,11] return true (left element = right element)**
obj=[11,12,21,11] return false (not verify them as a string)
My code fails the bolded ones and I've tried to work out why for ages!
Here's my code:
function sc(obj){
var obj2= obj;
if (obj==="") {
return true;
} else if (typeof obj === "string") {
var revd = obj.split("").reverse("").join("");
return revd === obj;
} else if (typeof obj === "number") {
var revd = parseInt(obj.toString().split("").reverse("").join(""));
return revd === obj;
} else if (typeof obj === "object") {
var obj2 = []
for (var i = obj.length-1; i >= 0; i--) {
obj2.push(obj[i])
}
return obj==obj2;
}
}
console.log(sc([11,12,13,12,11]));
Could anyone give me a hint as to what's going wrong?
instead of return obj==obj2;
write the following:
for (var i=0; i<obj.length, i++) {
if (obj[i] != obj2[i]) { return false; }
}
return true;
You cannot compare arrays like that since they are references. You can however, create a function for comparing them like this:
function arraysEqual(arr1, arr2) {
if(arr1.length !== arr2.length)
return false;
for(var i = arr1.length; i--;) {
if(arr1[i] !== arr2[i])
return false;
}
return true;
}
And then use it like this:
function arraysEqual(arr1, arr2) {
if(arr1.length !== arr2.length)
return false;
for(var i = arr1.length; i--;) {
if(arr1[i] !== arr2[i])
return false;
}
return true;
}
function sc(obj){
var obj2= obj;
if (obj==="") {
return true;
} else if (typeof obj === "string") {
var revd = obj.split("").reverse("").join("");
return revd === obj;
} else if (typeof obj === "number") {
var revd = parseInt(obj.toString().split("").reverse("").join(""));
return revd === obj;
} else if (typeof obj === "object") {
var obj2 = []
for (var i = obj.length-1; i >= 0; i--) {
obj2.push(obj[i])
}
return arraysEqual(obj, obj2);
}
}
console.log(sc([11,12,13,12,11]));
Just iterate forward and backward at the same time and check.
Here is a alrothim written in c, easy to convert to C++.
Checking if an array in C is symmetric
(Javascript makes life easy re-write to JavaScript and use !=)