Condition Spread based on Object props - javascript

I'm looking for way to remove empty or null props, on my example obj2, I want to avoid copying the birthPlace property or any other prop that comes empty.
const obj1 = { firstName: 'Foo', age: 22 };
const obj2 = { lastName: 'Bar', gender: 'M', birthPlace: '' };
const newObj = { ...obj1, ...obj2 };
Desired result:
{firstName: 'Foo', age: 22, lastName: 'Bar', gender: 'M'}
Is it possible using Conditional Objects props using Spread Operators in javascript?
const updateUserObj = {
...(obj1 !== check here<hasPropEmpty> && obj2)
}

There's no shorthand for it, but you can easily write a function that filters out those properties.
function nonEmptyProps(obj) {
return Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== null && v !== ''));
}
const obj1 = { firstName: 'Foo', age: 22 };
const obj2 = { lastName: 'Bar', gender: 'M', birthPlace: '' };
const newObj = {...nonEmptyProps(obj1), ...nonEmptyProps(obj2)};
console.log(newObj);

Using Object#entries you get the key-value pairs of an object, then, using Array#filter you iterate over these pairs to filter out the ones with empty values. Then, using Object#fromEntries you construct back the resulting pairs to an object.
const filterProps = (obj = {}) =>
Object.fromEntries(
Object.entries(obj).filter(([key, value]) =>
value !== null && value !== undefined && value !== ''
)
);
const obj1 = { firstName: 'Foo', age: 22 };
const obj2 = { lastName: 'Bar', gender: 'M', birthPlace: '' };
const newObj = { ...filterProps(obj1), ...filterProps(obj2) };
console.log(newObj);

Related

How to compare two objects but skip values and just compare keys and key types?

I want to write unit testing using Jest for my Node.js functions and need to test if response object has some specific keys or not. Something like this:
expect(await mokChannelsService.getChannel(inputDto)).toEqualKeys(outputDto);
// Or just a normal function
const result = await mokChannelsService.getChannel(inputDto);
const equal = isEqualKeys(result, outputDto);
This toEqualKeys function should check equality just for keys not values. And these objects are not instances of a class. for example these two objects should be equal:
const object1 = {
names: {
firstName: '',
lastName: '',
},
phone: 1,
};
const object2 = {
names: {
firstName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const object3 = {
names: {
firstName: 'John',
lastName: 12,
},
mobile: '12345',
};
const result1 = toEqualKeys(object1, object2); // true
const result1 = toEqualKeys(object1, object3); // false
Is there any NPM packages available for this?
You can iterate each of the keys in object1, checking that the same key exists in object2 and - if the associated property in object1 is an object, that all the keys from that object also exist in a similar object in object2:
const object1 = {
names: {
firstName: '',
lastName: '',
},
phone: 1,
};
const object2 = {
names: {
firstName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const object3 = {
names: {
firstName: 'John',
lastName: 12,
},
mobile: '12345',
};
const toEqualKeys = (obj1, obj2) => Object.keys(obj1)
.every(k => k in obj2 &&
(typeof obj1[k] != 'object' || typeof obj2[k] == 'object' && toEqualKeys(obj1[k], obj2[k]))
)
console.log(toEqualKeys(object1, object2))
console.log(toEqualKeys(object1, object3))
You can get more detail on the differences by logging differences into an array (using reduce to iterate the keys in the reference object). This function will return an array of errors (empty if there are none) for each comparison:
const object1 = {
names: {
firstName: '',
lastName: '',
},
phone: 1,
};
const object2 = {
names: {
firstName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const object3 = {
names: {
firstName: 'John',
lastName: 12,
},
mobile: '12345',
};
const object4 = {
names: 'John',
mobile: '12345',
};
const object5 = {
names: {
firstName: 'John',
lastName: 12,
},
phone: {
landline : '12345',
mobile : '555'
}
};
const object6 = {
names: {
firtName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const notEqualKeys = (obj1, obj2, prefix = '') => Object.keys(obj1)
.reduce((acc, k) => {
if (!(k in obj2)) acc.push(`key ${k} not present in obj2${prefix}`)
if (typeof obj1[k] == 'object') {
if (typeof obj2[k] == 'object') {
acc = acc.concat(notEqualKeys(obj1[k], obj2[k], `${prefix}.${k}`))
}
else {
acc.push(`${k} is an object in obj1${prefix} but a value in obj2${prefix}`)
}
}
else if (typeof obj2[k] == 'object') {
acc.push(`${k} is a value in obj1${prefix} but an object in obj2${prefix}`)
}
return acc
}, [])
console.log((arr = notEqualKeys(object1, object2)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object3)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object4)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object5)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object6)).length ? arr : true)

Js ES6, Merge 2 objects and store in array instead replacing existent key

i have the following
let user = { name: 'John', lastName: 'Doe', phone: '000111222' };
let otherInfo = { phone: '123456', age: '30' };
After merging both
let result = { ...user, ...otherInfo };
I got
{ name: 'John', lastName: 'Doe', phone: '123456', age: '30' };
Base on this, the phone is duplicated entry, i want to keep the old phone and keep both in array, based on ES6 spread, note: (i can do iterating object, but is better to write less code)
{ name: 'John', lastName: 'Doe', phone: ['000111222', '123456'], age: '30' };
Some ideas?, thank you in advance.
Merge the objects together, and get all unique keys with Object.keys(). Reduce the keys, and check if the key exists in the objects. If it exists in both of them, combine the values to an array. If it doesn't take the value from one of the objects:
const fn = (o1, o2) => Object.keys({ ...o1, ...o2 }) // get combined keys
.reduce((acc, key) => {
// if key exists it both object combine the values in an array
if (key in o1 && key in o2) acc[key] = [o1[key], o2[key]];
// take the value from o1 or o2
else acc[key] = key in o1 ? o1[key] : o2[key];
return acc;
}, {});
const user = { name: 'John', lastName: 'Doe', phone: '000111222' };
const otherInfo = { phone: '123456', age: '30' };
const result = fn(user, otherInfo);
console.log(result);
You can do this:
let result = { ...user, ...otherInfo, phone: user.phone && otherInfo.phone ? [user.phone, otherInfo.phone]: user.phone || otherInfo.phone };
For a generic solution, where you would maybe need to merge more than two objects, you could apply Object.entries on each of the objects and create a flat array of the resulting pairs. Then use these pairs to extend the result object. When a pair references a key that is already in the result object, use [].concat to silently merge that value with the new value into an array. This also works if the stored value was already an array with two or more previously found values.
Here is a snippet:
const user = { name: 'John', lastName: 'Doe', phone: '000111222' };
const otherInfo = { phone: '123456', age: '30' };
const moreInfo = { phone: '9876543', hometown: 'Paris' };
let res = {};
for (let [k, v] of [user, otherInfo, moreInfo].flatMap(Object.entries)) {
res[k] = k in res ? [].concat(res[k], v) : v;
}
console.log(res);
THIS
1 line of code
let result = Object.assign({}, info, otherInfo, {phone: [info.phone, otherInfo.phone]})
the Object.Assign is more faster

Create array from the values that are looped in for...in

I was getting this weird result when I'm trying to construct an array of objects by using for...in loop for a object
const obj = {
name: 'John',
age: '25'
}
for (const property in obj) {
const obj1 = {
prop1: property,
prop2: obj[property]
}
const result = [].push(obj)
console.log(result)
}
I was expecting the result to be
[{prop1: 'name', prop2: 'John'}, {prop1: 'age', prop2: '25'}]
Could anyone please help?
push returns the new length of the array, which is why you see 1. Move the array initialization out of the loop and log after the loop is finished:
const obj = {
name: 'John',
age: '25'
}
const result = []
for (const property in obj) {
const obj1 = {
prop1: property,
prop2: obj[property]
}
result.push(obj1)
}
console.log(result)
You are pushing to a new Array that is scoped inside of your loop. You really want to do:
const obj = {
name: 'John',
age: '25'
}
const results = [];
for(let p in obj){
results.push({
prop1: p,
prop2: obj[p]
});
}
console.log(results)

How to replace JSON object kyes

I have two arrays:
[{
key: 'first_name',
value: 'FirstName'
}, {
key: 'middle_name',
value: 'MiddleName'
}, {
key: 'age',
value: 'Age'
}]
And:
[{
FirstName: "Mayuresh",
MiddleName: "Dinkar ",
LastName: "Joshi",
Age: 4
},
{
FirstName: "Arun",
MiddleName: "Vikas",
LastName: "Pathak",
Age: 25
},
{
FirstName: "Narendra",
MiddleName: "Damodardas",
LastName: "Modi",
Age: 50
}
]
in angular i want to compare both array and replace first object key with 2nd object key
i tried below code but it is not working
var updatedBooks = JSON.parse(parsed, function (key, value) {
// if key is `Number`, change it to ISBN
let data = _.find(arr, function (o) { return o.value==key });
//console.log("data",data);
if (key === data.value) {
data.key = value;
} else {
return value;
}
});
Expected output:
[{
first_name: "Mayuresh",
middle_name: "Dinkar ",
LastName: "Joshi",
age: 24
}, {
first_name: "Arun",
middle_name: "Vikas",
LastName: "Pathak",
age: 25
}, {
first_name: "Narendra",
middle_name: "Damodardas",
LastName: "Modi",
age: 50
}]
Use map with find and Object.entries:
const arr = [{key:'first_name',value:'FirstName'},{key:'middle_name',value:'MiddleName'},{key:'age',value:'Age'}];
const data = [{FirstName:"Mayuresh",MiddleName:"Dinkar ",LastName:"Joshi",Age:4},{FirstName:"Arun",MiddleName:"Vikas",LastName:"Pathak",Age:25},{FirstName:"Narendra",MiddleName:"Damodardas",LastName:"Modi",Age:50}];
const res = data.map(elem => {
let temp = {};
Object.entries(elem).forEach(([k, v]) => {
let newKey = arr.find(({ value }) => value == k);
if (newKey) temp[newKey.key] = v;
else temp[k] = v;
});
return temp;
});
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }
The callback of JSON.parse just works on values. Therefore in your case the value you want to work with is the object, not the objects key-value pairs:
const result = JSON.parse(data, (key, value) => {
if(typeof value !== "object" || value === null || Array.isArray(value))
return value;
const result = {};
for(const [key, value] of Object.entries(value)) {
const replaceKey = (keys.find(it => it.value === key) || {}).key;
result[replaceKey || key] = value;
}
return result;
});
Create a Map of value -> key from the keys array. Then map the data, and use _.mapValues() to replace the keys of the objects:
const keys = [{"key":"first_name","value":"FirstName"},{"key":"middle_name","value":"MiddleName"},{"key":"age","value":"Age"}]
const data = [{"FirstName":"Mayuresh","MiddleName":"Dinkar ","LastName":"Joshi","Age":4},{"FirstName":"Arun","MiddleName":"Vikas","LastName":"Pathak","Age":25},{"FirstName":"Narendra","MiddleName":"Damodardas","LastName":"Modi","Age":50}]
const keysMap = new Map(keys.map(o => [o.value, o.key]))
const result = data.map(o => _.mapKeys(o, (v, k) => keysMap.get(k) || k))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
And the same idea with lodash/fp:
const { map, props, mapKeys } = _
const keys = [{"key":"first_name","value":"FirstName"},{"key":"middle_name","value":"MiddleName"},{"key":"age","value":"Age"}]
const data = [{"FirstName":"Mayuresh","MiddleName":"Dinkar ","LastName":"Joshi","Age":4},{"FirstName":"Arun","MiddleName":"Vikas","LastName":"Pathak","Age":25},{"FirstName":"Narendra","MiddleName":"Damodardas","LastName":"Modi","Age":50}]
const keysMap = new Map(map(props(['value', 'key']), keys))
const result = map(mapKeys(k => keysMap.get(k) || k), data)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
var arr1 = [{
key: 'first_name',
value: 'FirstName'
}, {
key: 'middle_name',
value: 'MiddleName'
}, {
key: 'age',
value: 'Age'
}];
var arr2 = [{
FirstName: "Mayuresh",
MiddleName: "Dinkar ",
LastName: "Joshi",
Age: 4
},
{
FirstName: "Arun",
MiddleName: "Vikas",
LastName: "Pathak",
Age: 25
},
{
FirstName: "Narendra",
MiddleName: "Damodardas",
LastName: "Modi",
Age: 50
}
];
Let's take an array and put the first array value as key and key as value!!
var arr1Obj = {};
arr1.forEach(e => arr1Obj[e.value] = e.key);
Now the object that we create above looks somewhat like following
var result = [];
Now it's time to iterate over the second array.
arr2.forEach(elem => {
var obj = {};
// here elem is an object having properties,
// iterate over it as well for changing the prop nam
Object.keys(elem).forEach(key => {
var propKey = arr1Obj[key];
if(propKey) {
obj[propKey] = elem[key];
} else {
obj[key] = elem[key];
}
});
result.push(obj);
})
console.log(result)

how to destruct part of properties from an object

For example, I got an object like this:
obj1 = {
name: 'Bob',
age: 20,
career: 'teacher'
}
Now I need to duplicate part of its properties instead all of them.
obj2 = {
name: '',
age: '',
}
I know I can do it like obj2.name = obj1.name, which will be verbose if many properties need to be duplicated. Are there any other quick ways to solve this problem?
I tried
let {name: obj2.name, age: obj2.age} = obj1;
but got error.
Actually you don't need object destructuring, just simple assignment:
obj2 = { name: obj1.name, age: obj1.age }
Now, obj2 holds wanted properties:
console.log(obj2);
// Prints {name: "Bob", age: 20}
If you want to merge old properties of obj2 with new ones, you could do:
obj2 = { ...obj2, name: obj1.name, age: obj1.age }
Drop the let (you're not declaring variables) and surround with parentheses:
({name: obj2.name, age: obj2.age} = obj1);
I guess you can use ES6 Object destructuring syntax
var obj = { name: 'kailash', age: 25, des: 'backenddev'}
({name} = obj);
You could use the target object as template for the properties and assign the values of obj1 with a default value of the target object.
var obj1 = { name: 'Bob', age: 20, career: 'teacher' },
obj2 = { name: '', age: '' };
Object.keys(obj2).forEach(k => obj2[k] = obj1[k] || obj2[k]);
console.log(obj2);
Another solution would be to write a reuable method for this. You can supply an object and the methods you would like to copy.
const duplicatePropertiesFromObject = (obj, propertyNames = [], newObj = {}) => {
Object.keys(obj).forEach((key) => {
if (propertyNames.includes(key)) {
newObj[key] = obj[key];
}
});
return newObj;
}

Categories

Resources