I have an object like this
const obj = {a: 123, b: 'text', c: [1,2,3]}
I use it for route params, but c can be an empty array, I don't have to handle a and b because they surely will have value. Do I have to manually omit c from obj? making it only {a: 123, b: 'text'}?
Is there any way I don't have to manually omit the property from the object if the value is undefined?
If you're using lodash, you can use omitBy
_.omitBy({a: 123, b: 'text', c: undefined}, _.isUndefined)
Morever, you can combine many condition by chain
_({a:123,b:'text',c:undefined,d:null})
.omitBy(_.isUndefined)
.omitBy(_.isNull).value();
Or
You can introduce a function that help u omit undefined value for every objects
const obj = {a: 123, b: 'text', c: undefined};
const omitObj = obj => Object.keys(obj).reduce((acc, key) => {
if (obj[key] === undefined) {
return acc;
}
acc[key] = obj[key];
return acc;
}, {})
const newObj = omitObj(obj);
console.log(newObj);
In case you're not sure about the last param
just make a conditional statement
const obj = {a: 123, b: 'text', c: [1,2,3]}
var AnotherObj = obj
if (AnotherObj.c == undefined)
delete AnotherObj.c
if obj.c is defined everything will be fine
else it will delete the third param from the object
Related
I'm trying to make a deep copy of an object without using lodash.
I'm unable to think of any case where this will fail. Can someone suggest me any edge case where this may fail?
let Obj = {
a: "Hello",
b: {
c: {
h: "World"
},
d: null
},
e: () => 'me',
f: new Date(),
g: [1, 2, 3]
}
let result = JSON.parse(JSON.stringify(Obj));
for (let key in Obj) {
if (!result.hasOwnProperty(key) || Obj[key] instanceof Date) {
result[key] = Obj[key];
}
}
console.log(result)
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'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
I have two objects:
obj1 = {
a: 1,
b: 2,
c: 3,
d: 4
}
and
obj2 = {
a: "foo",
c: "bar"
}
From those I want to create a third object:
obj3 = {
b: 2,
d: 4
}
The third object should contain all elements from the first object, except those that share their key with an element in the second object.
I know ways to achieve this if the excluded keys are hard coded:
const { a, c, ...obj3 } = obj1;
or
const obj3 = Object.assign({}, obj1);
[ "a", "c" ].forEach(key => delete obj3[key]);
Is there a way to do this if the excluded keys are not hard coded but instead also keys of another object, like I described above?
The reason I need to do this is because in my react app I want to spread all props given to a container component to it's child component, except the props defined in the container component's PropTypes (the props used by the container component that shouldn't be passed on).
Your last example is a good start. You can combine it with Object.keys to achieve what you want:
let obj1 = {
a: 1,
b: 2,
c: 3,
d: 4
}
let obj2 = {
a: "foo",
c: "bar"
}
const obj3 = Object.assign({}, obj1);
Object.keys(obj2).forEach(key => delete obj3[key]);
console.log(obj3);
Object.keys applied to an object returns a string array containing the keys of the object.
Iterate through the keys of the first object obj1 and check whether the key is present in obj2, if not present in both obj1 and obj2 add it to the third obj3.
let obj1 = {
a: 1,
b: 2,
c: 3,
d: 4
}
let obj2 = {
a: "foo",
c: "bar"
}
let obj3 = {};
Object.keys(obj1).forEach((key)=>{ if(!(key in obj2)){
obj3[key] = obj1[key];
}
});
You can use Object.entries() with Array.reduce(), and object spread to construct a new object from all the keys of obj1 that don't exist on obj2:
const objectDiff = (o1, o2) =>
Object.entries(o1) // get an array of key/value pairs
.reduce((r, [k, v]) => ({
...r, // add the previous state of the object
...k in o2 || { [k]: v } // if key is not in the 2nd object add it
}), {});
const obj1 = {
a: 1,
b: 2,
c: 3,
d: 4
}
const obj2 = {
a: "foo",
c: "bar"
}
const result = objectDiff(obj1, obj2);
console.log(result);
Approach 1: using Object.keys:
const obj4 = Object.assign({}, obj1);
Object.keys(obj2).forEach(key => delete obj4[key]);
Approach 2: Another cleaner way, using for in loop with hasOwnProperty:
function filterKeys(obj1, obj2) {
const returnObj = {};
for (const prop in obj1) {
if (obj1.hasOwnProperty(prop) && !obj2.hasOwnProperty(prop)) {
console.log(`obj1.${prop} = ${obj1[prop]}`);
returnObj[prop] = obj1[prop];
}
}
return returnObj;
}
You can also use Object.keys, Array.prototype.filter and Array.prototype.reduce to achieve the desired output:
const obj1 = {a: 1,b: 2,c: 3,d: 4}
const obj2 = {a: "foo",c: "bar"}
const obj3 = Object.keys(obj1).filter(k => !obj2.hasOwnProperty(k)).reduce((a,e) => (a[e] = obj1[e], a),{});
console.log(obj3);
There is _.merge functionality in lodash. I want to achieve the same thing in ES6 or ES7.
Having this snippet:
Object.assign({}, {key: 2}, {key: undefined})
I want to receive {key: 2}. Currently I receive {key: undefined}
This is NOT a deep merge.
Is it possible? If yes then how to achieve that?
You can't achieve that with a straight usage of Object.assign, because each next object will rewrite the same keys for prev merge. The only way, to filter your incoming objects with some hand-crafted function.
function filterObject(obj) {
const ret = {};
Object.keys(obj)
.filter((key) => obj[key] !== undefined)
.forEach((key) => ret[key] = obj[key]);
return ret;
}
You can simply filter out the keys with undefined values before passing them to Object.assign():
const assign = (target, ...sources) =>
Object.assign(target, ...sources.map(x =>
Object.entries(x)
.filter(([key, value]) => value !== undefined)
.reduce((obj, [key, value]) => (obj[key] = value, obj), {})
))
console.log(assign({}, {key: 2}, {key: undefined}))
Write a little utility to remove undefined values:
function removeUndefined(obj) {
for (let k in obj) if (obj[k] === undefined) delete obj[k];
return obj;
}
Then
Object.assign({}, {key: 2}, removeUndefined({key: undefined}))
This seems preferable to writing your own assign with wired-in behavior to remove undefined values.
use lodash to omit nil values and then combine the two objects into one via spread
{ ...(omitBy({key: 2}, isNil)), ...(omitBy({key: undefined}, isNil))}
See more info on lodash here https://lodash.com/docs/4.17.15
With ES2019/ES10's new object method, Object.fromEntries(), MichaĆ's answer can be updated:
const assign = (target, ...sources) =>
Object.assign(target, ...sources.map(x =>
Object.fromEntries(
Object.entries(x)
.filter(([key, value]) => value !== undefined)
)
))
console.log(assign({}, {key: 2}, {key: undefined}))
If you just need the values and don't need an object, you could also use object destructuring:
const input = { a: 0, b: "", c: false, d: null, e: undefined };
const { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 } = input;
console.log(a, b, c, d, e, f);
// => 0, "", false, null, 5, 6
This will only override absent or undefined values.
I often use this for function argument default values like this:
function f(options = {}) {
const { foo = 42, bar } = options;
console.log(foo, bar);
}
f();
// => 42, undefined
f({})
// => 42, undefined
f({ foo: 123 })
// => 123, undefined
f({ bar: 567 })
// => 42, 567
f({ foo: 123, bar: 567 })
// => 123, 567