Related
I have two objects x,y and i want to compare both of these excluding one of the keys "c"
let x = {a: 5, b: 6, c: "string"}
let y = {a: 5, b: 8, c: "string"}
How i am trying to compare it is -
JSON.stringify(x) === JSON.stringify(y)
Above works but it will compare all keys I want to exclude c in comparison here. What's the best way to achieve this ?
The following code obtains all the keys from object x, removes c from the list, and then performs the comparison.
let x = { a: 5, b: 6, c: "string" };
let y = { a: 5, b: 6, c: "another string" };
console.log(
Object.keys(x)
.filter((key) => key !== "c")
.every((key) => x[key] === y[key])
);
array.sort requires a comparator function. You can use the exact same thing here
function myObjComparator (x, y) {
if (x.a != y.a) return y.a - x.a;
return y.b - x.b;
}
Generally, I would NOT stringify objects in order to compare them.
The reason is quite simple and is that you MUST to be sure that the order of the members are the same, otherwise the result won't be correct.
Ex:
// objects are equals
const y = { a: '1', b: '2' }
const x = { b: '2', b: '1' }
// but result is not what you expect
JSON.stringify(x) === JSON.stringify(y) // false
But, if you can ensure that the order of the members is always the same, stringify is fast and a good option.
You can use the spread syntax in order to avoid the "c" property
const remappedX = { ...x, c: undefined };
const remappedY = { ...y, c: undefined };
JSON.stringify(remappedX) === JSON.stringify(remappedY); // true
Or alternatively
const allowedKeys = Object.keys(x).filter((k) => k !== 'c');
JSON.stringify(x, allowedKeys) === JSON.stringify(y, allowedKeys);
More generic method is to loop over Object key or alternatively to entries
for(const key of Object.keys(x)) {
if (key === 'c') {
continue;
}
if (x[key] !== y[key]) {
return false;
}
}
return true;
But, if your object is nested, you need to use a deep equality algorithm, which is of course slower.
More generic answer has been given here
Thanks everyone for quick responses on my question but below was an easy method from which I could implement the above logic
import omit from "lodash/omit";
import isEqual from "lodash/isEqual";
let x = {a: 5, b: 6, c: "string"},
y = {a: 5, b: 8, c: "string"}
result = isEqual(omit(x, ['c']), omit(y, ['c']))
What about this?
JSON.stringify(x, ['a','b']) === JSON.stringify(y, ['a','b']);
better solution:
function replacer(key, value) {
// Filtering out properties
if (key === 'c') {
return undefined;
}
return value;
}
JSON.stringify(x, replacer) === JSON.stringify(y, replacer);
correct better solution:
const filterKeys = (obj) => Object.keys(y).filter(k => k !== 'c').sort()
JSON.stringify(x, filterKeys(x)) === JSON.stringify(y, filterKeys(y));
Using ternary operator to compare both the objects.
Demo :
let x = {a: 5, b: 6, c: "string"};
let y = {a: 5, b: 8, c: "string"};
function compare(obj1, obj2) {
return (obj1.a != obj2.a) ? false : (obj1.b != obj2.b) ? false : true;
}
console.log(compare(x, y));
By deleting the unwanted properties and then compare using JSON.stringify()
Demo :
let x = {a: 5, b: 6, c: "string"};
let y = {a: 5, b: 8, c: "string"};
delete x.c;
delete y.c;
console.log(JSON.stringify(x) === JSON.stringify(y));
Here is a solution
const x = {a: 5, b: 8, c: "string1", d: {e: 9}}
const y = {a: 5, b: 8, c: "string2", d: {e: 9}}
const compare = (obj1, obj2, except = []) => {
if (Object.keys(obj1).length < Object.keys(obj2).length) {
[obj1, obj2] = [obj2, obj1];
}
for (const obj1Key in obj1) {
if (except.includes(obj1Key)) continue;
if (!obj2.hasOwnProperty(obj1Key)) return false;
if (typeof obj1[obj1Key] === 'object') {
if (typeof obj2[obj1Key] !== 'object') return false;
const isEqual = compare(obj1[obj1Key], obj2[obj1Key]);
if (isEqual) continue
return false;
}
if (obj1[obj1Key] !== obj2[obj1Key]) return false;
}
return true;
}
console.log(compare(x, y, ['c']));
I have a javascript plain object like this one: {a: {b: 1} }
and I want to convert it to a dot-notation string like this a.b = 1
use case:
sending the object to a plain-text environment such as cli or as a url parameter.
It's rather hard to tell whether this is what you want, but something like this would flatten a tree of objects into a list of dotted paths...
var data = {
a: {
b: 1,
c: {
d: 8
}
},
e: {
f: {
g: 9
},
h: 10,
i: [1, 2, 3]
}
};
function toDotList(obj) {
function walk(into, obj, prefix = []) {
Object.entries(obj).forEach(([key, val]) => {
if (typeof val === "object" && !Array.isArray(val)) walk(into, val, [...prefix, key]);
else into[[...prefix, key].join(".")] = val;
});
}
const out = {};
walk(out, obj);
return out;
}
console.log(toDotList(data));
I'm searching a for a way to create a function. in which I can pass an object and an array of properties (keys) I want gone. That function will return me a new object that doesn't have the keys I've specified.
function(keys: array, obj: object) {...}
Question is - how do I do that with multiple properties?
I've searched and only found this kind of solution:
const myObject = {
a: 1,
b: 2,
c: 3
};
const { a, ...noA } = myObject;
But it only works if I want to remove only ONE key. What if I want to remove multiple, using an array I just passed? How do I do that without mutating the original array or manually creating copies of it?
You could destructure the object by taking a computed property for unwanted properties.
const
without = (object, keys) => keys.reduce((o, k) => {
const { [k]: _ , ...p } = o;
return p;
}, object),
myObject = { a: 1, b: 2, c: 3 },
keys = ['a', 'b'],
result = without(myObject, keys);
console.log(result);
You can do it using reduce and Object.entries(). You can try this:
const myObject = {
a: 1,
b: 2,
c: 3,
d: 4
};
const removeProps = (object, keys) => {
return Object.entries(object).reduce((a, [key, value]) => (keys.indexOf(key) === -1 ? {...a, [key]: value}: a), {});
}
console.log(removeProps(myObject, ['b']));
console.log(removeProps(myObject, ['b', 'c']));
console.log('Original Object: ', myObject);
.as-console-wrapper{min-height: 100%!important; top: 0}
Above answers are great, I'm sharing my try:
var myObject = { a: 1, b: 2, c: 3, d: 4};
let remove=(obj, arr)=> {
let output=[];
for(const [key, value] of Object.entries(obj)){
if(!arr.includes(key)){
output.push([key,value]);
}
}
return Object.fromEntries(output);
}
console.log(remove(myObject, ['a']));
console.log(remove(myObject, ['a', 'c']));
This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 3 years ago.
I am trying to implement a clone function but I am not sure if I am doing it right while trying to clone '[object Function]'. You will see the result at the bottom. I am not sure if desired result should look like the original input data. Let me know what you think and if you have any ideas on how to implement it. Here is the code.
UPD: actually it works as it supposed to be working. I am going to leave it here so people can use it if they have the same question.
function deep(value) {
if (typeof value !== 'object' || value === null) {
return value;
}
if (Array.isArray(value)) {
return deepArray(value);
}
return deepObject(value);
}
function deepObject(source) {
const result = {};
Object.keys(source).forEach(key => {
const value = source[key];
result[key] = deep(value);
});
return result;
}
function deepArray(collection) {
return collection.map(value => {
return deep(value);
});
}
const id1 = Symbol('id');
const value = {
a: 2,
f: id1,
b: '2',
c: false,
g: [
{ a: { j: undefined }, func: () => {} },
{ a: 2, b: '2', c: false, g: [{ a: { j: undefined }, func: () => {} }] }
]
};
RESULT
{ a: 2,
f: Symbol(id),
b: '2',
c: false,
g:
[ { a: { j: undefined }, func: [Function: func] },
{ a: 2,
b: '2',
c: false,
g: [ { a: { j: undefined }, func: [Function: func] } ] } ] }
You cannot clone an arrow function, when you clone an object that has arrow functions as properties they will always be bound to the object they were created in, you cannot rebind them, that is the whole point of an arrow function, predictable behaviour of the this object. If you want to clone objects then make sure that any functions that refer to this are normal functions and not arrow functions.
Better use below single code for deepcopy -
function deepCopy(oldObj) {
var newObj = oldObj;
if (oldObj && typeof oldObj === "object") {
newObj = Object.prototype.toString.call(oldObj) === "[object Array]" ? [] : {};
for (var i in oldObj) {
newObj[i] = this.deepCopy(oldObj[i]);
}
}
return newObj;
}
I'd like to merge two similar but not identical objects and override null values in one of them, if such exist. For example I'd have these two objects:
const obj1 = {
a: 1,
b: '',
c: [],
d: null
}
const obj2 = {
a: 2,
b: null,
d: 1
}
And the effect of merge should be:
const objMerged = {
a: 2,
b: '',
c: [],
d: 1
}
In other words, the most important source of data in the merged object is obj2 but it lacks some properties from obj1, so they need to be copied and also some of the obj2 values are null so they should be taken from obj1 as well.
EDIT
I tried:
_.extend({}, obj1, obj2)
and
Object.assign({}, obj1, obj2)
You could also mix and match with ES6 destructuring and lodash _.omitBy:
const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }
const result = {..._.omitBy(obj1, _.isNull), ..._.omitBy(obj2, _.isNull)}
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could also do it with ES6 only like this:
const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }
let omitNull = obj => {
Object.keys(obj).filter(k => obj[k] === null).forEach(k => delete(obj[k]))
return obj
}
const result = { ...omitNull(obj1), ...omitNull(obj2) }
console.log(result)
To add to this list of good answers, here's a recursive solution that will work with nested structures.
This example will merge the common properties of the dst object to the src object in all levels of nesting, leaving any properties that are not common intact.
const merge = (dst, src) => {
Object.keys(src).forEach((key) => {
if (!dst[key]) {
dst[key] = src[key];
} else if (typeof src[key] === 'object' && src[key] !== null && typeof dst[key] === 'object' && dst[key] !== null) {
merge(dst[key], src[key]);
}
});
},
/* Usage: */
src = {
prop1: '1',
prop2: {
val: 2,
}
},
dst = {
prop1: null,
prop2: {
val: null,
},
prop3: null,
};
merge(dst, src);
console.log(dst);
You can use _.mergeWith(), and in the merge callback only take the 2nd value if it's not null:
const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }
const result = _.mergeWith({}, obj1, obj2, (o, s) => _.isNull(s) ? o : s)
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
Here is a pure JS based solution:
Iterate through the first object to replace values from second object, then add the additional values from the second object.
const obj1 = {
a: 1,
b: '',
c: [],
d: null
}
const obj2 = {
a: 2,
b: null,
d: 1
}
function mergeObjs(obj1, obj2){
const merged = {}
keys1 = Object.keys(obj1);
keys1.forEach(k1 => {
merged[k1] = obj2[k1] || obj1[k1]; // replace values from 2nd object, if any
})
Object.keys(obj2).forEach(k2 => {
if (!keys1.includes(k2)) merged[k2] = obj[k2]; // add additional properties from second object, if any
})
return merged
}
console.log(mergeObjs(obj1, obj2))
Using Lodash by create() and omitBy()
const obj1 = {"a":1,"b":"","c":[],"d":null}
const obj2 = {"a":2,"b":null,"d":1}
const objMerged = _.create(
_.omitBy(obj1, _.isNull),
_.omitBy(obj2, _.isNull)
)
console.log(objMerged)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
If you're interested in only the first level of the two objects you could do something like this:
const obj1 = {
a: 1,
b: '',
c: [],
d: null
}
const obj2 = {
a: 2,
b: null,
d: 1
}
const merged = Object.keys(obj1).concat(Object.keys(obj2)) // create an array that contains the keys of the two objects.
.filter((k, i, arr) => arr.indexOf(k) === i) // remove duplicate keys
.reduce((a, c) => {
a[c] = obj1[c] !== null ? obj1[c] : obj2[c];
return a;
}, {});
console.log(merged);
This example only check for null values, you should probably extend it to check for others like undefined, empty strings, etc.
You did it the good way using Object.assign, just remove what you don't want right before
Object.keys(obj1).forEach( k => {
if ( obj1[k] //write the condition you want
delete obj1[k]
});
var objMerged = {};
for (var kobj1 in obj1) {
for (var kobj2 in obj2) {
if (obj1[kobj1] == null && obj2[kobj1] != null)
objMerged[kobj1] = obj2[kobj1];
else if (obj2[kobj2] == null && obj1[kobj2] != null)
objMerged[kobj2] = obj1[kobj2];
}
}
//Print objMerged to display