JS Recursive object assign [duplicate] - javascript

This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 4 years ago.
I learned that when using Object.assign() it extends only the top level object. How can I deeply extend the object? For example, let's say I have the following source object:
const source = {
id: 1,
otherKey: {},
params: {
page: {
a: 1,
b: {}
},
data: {
b: 1
}
}
}
And I am using Object.assign() like this:
Object.assign({}, source, {
params: {
page: {
a: 2
}
}
}
The result will be:
{
id: 1,
otherKey: {},
params: {
page: {
a: 2
}
}
}
How can I preserve the params.data key and the params.page.b key in a shallow clone way.
oldObject.params.data === newObject.params.data // true
oldObject.params.page === newObject.params.page // false
oldObject.params.page.b === newObject.params.page.b // true
Note: This question is not the same as How to deep merge instead of shallow merge. The answers there does not give the expected results.
Check this bin that takes an answer from the above link.

So in your posted code, what happens is, if the source and target both contain the same key then nothing happens. The object is recursed down to the children. However if you simply change the inner block to this:
if (!target[key]) {
Object.assign(target, { [key]: {} });
}else{
target[key] = Object.assign({}, target[key])
}
mergeDeep(target[key], source[key]);
This will create a new assigned object for any key that is found in both the source and the target. Interestingly though, if you do this, your expected falseys will not show up in the console. This is because the target will always match the result, as it is the final mutated object. So in order to get your expected false results, you need to do something like the following:
var tester = source.params
const result = mergeDeep(source, b)
console.log(tester === result.params) // this will be false after the above addition
You can see your desired result here: http://jsbin.com/vicemiqiqu/1/edit?js,console

Related

Update object with new changes that don't override unchanged values Vue.js 2

I'm having an issue where a payload I'm receiving is not updating the values correctly for an object before passing it to the database. In other words, the new changes are not persisting for fields have new or changed values. I'm using Vue.js 2. How do I successfully update the incoming object and storing those values in an existing object with changes?
More information: We receive an object from an API that may have existing keys with values or none at all if the meeting matches certain characteristics - like the username/birthday/phone number. The form is supposed to pass the new key/values for the personal information if its changed. Instead of it doing that, the data is keeping the old changes and not updating the values for the new changes. userPersonalInfo is not updating in this case.
ModalVerification.vue
onVerifySuccess(existingData) {
// if no object exist, complete new form
if(!Object.keys(existingData).length) {
this.completeFormModal();
} else {
this.meetingDetails.is_authenticated_user = true;
this.updateMeetPlanInformation(this.getMeetingPlanFields(existingData);
// only return existing data if object not null // else update the existing data with new key/value pairs. Most likely wrong, because its not checking if any values in the object have been updated before passing.
const userPersonalInfo = (existingData) === null ? this.getUserPersonalInfo(existingData) : existingData;
vueAssign(this.meetingDetails, userPersonalInfo);
this.completeFormModal();
}
export function vueAssign(objVal, srcVal) {
Object.keys(srcVal).forEach((key) => {
Vue.set(objVal, key, srcVal[key]);
});
}
The problem is likely in vueAssign, but you haven't shown that method. I can still suggest solutions:
Object.assign
Use Object.assign to copy props from meetingDetails into userPersonalInfo, overwriting any common properties:
Object.assign(userPersonalInfo, this.meetingDetails)
const userPersonalInfo = {
a: 1,
b: 2,
}
const meetingDetails = {
a: 999,
c: 'hello',
}
Object.assign(userPersonalInfo, meetingDetails)
console.log({ userPersonalInfo })
Spread operator
Use the spread operator, which performs the same assignment:
let userPersonalInfo = /*...*/
userPersonalInfo = {
...userPersonalInfo,
...this.meetingDetails,
}
let userPersonalInfo = {
a: 1,
b: 2,
}
const meetingDetails = {
a: 999,
c: 'hello',
}
userPersonalInfo = {
...userPersonalInfo,
...meetingDetails,
}
console.log({ userPersonalInfo })

Shallow copy JavaScript object without references

How can I shallowly copy an JavaScript object and get rid of all non-primitive values (all references), while keeping all properties of the given object. Values of the properties might turn to null in this process.
Object.assign, lodash clone and the spread operator allow us to get a shallow copy of an object. However, despite the naming, the result object is not shallow [fact]. It has copied all references too, so the whole object tree is still accessible.
For a analytics solution, I need to get rid of anything deeper than one level.
How can I do it (libraries are also ok, ES6 is fine) without writing dozens of rules to deal with all the possible data types? Ideally, objects and array properties are not lost, but replaced with something, e.g. null or empty objects/arrays.
Example
const source = {
nr: 1,
str: 'ok',
obj: {
uhOh: 'kill me'
},
arr: ['well ok', { uhOh: 'uhOh' }],
}
// apply voodoo
const expected = {
nr: 1,
str: 'ok',
obj: {},
arr: [],
}
// This would also be an valid result:
const expected = {
nr: 1,
str: 'ok',
obj: null,
arr: null,
}
You could loop through the keys of the object using for...in. If the value is an object, set the key to null in expected, else set the value in expected to the value from source
const source = {
nr: 1,
str: 'ok',
obj: {
uhOh: 'kill me'
},
arr: ['well ok', {
uhOh: 'uhOh'
}],
}
const expected = {};
for (const key in source) {
if (typeof source[key] === 'object')
expected[key] = null
else
expected[key] = source[key]
}
console.log(expected)
This is not an answer in it's own right, but an addendum to the excellent answer by #adiga, this time using typescript and a type parameter:
private primitiveClone<T>(source: T): T {
const dto = Object.assign({}, source);
for (const key in dto) {
if (typeof dto[key] === 'object') {
dto[key] = null;
}
}
return dto;
}
usage
var simpleClone = primitiveClone(data);

Can someone explain the use of ...spread operator in the following example? [duplicate]

This question already has answers here:
What are these three dots in React doing?
(23 answers)
Closed 3 years ago.
As per my understanding this is how spread operator works:
x=[1,2,3];
y=[...x,4,5];
// this is same as y=[1,2,3,4,5]
const initialState={
ingredients: [
new Ingredient('Apples', 5),
new Ingredient('Tomatoes', 10),
]
};
export function shoppingListReducer( state=initialState, action:ShoppingListActions.ShoppingListActions ) {
switch(action.type) {
case ShoppingListActions.ADD_INGREDIENT:
return {
...state,
ingredients:[...state.ingredients,action.payload ]
}
default:
return state;
}
Here in the above example what does
return {
...state,
ingredients:[...state.ingredients,action.payload ]
}
evaluate to?
Payload is of type Ingredient :
export class Ingredient {
constructor(public name: string, public amount: number) {}
}
Basically you are trying to create an object with all the properties of state object and overriding its property ingredients with value as all the values in state.ingredients array along with action.payload. This might be done to separate the reference of the result object from state object.
var state = {
"someprop" : "somevalue",
"ingredients" : ["a", "b"]
};
var action = {
"payload" : 4
};
var result = {
...state,
ingredients:[...state.ingredients,action.payload ]
};
state.someprop = "somevalue1"; // does not alter result object
state.ingredients.push("c"); // does not alter result object
console.log(result);
Alternatively, to understand it better, you can break that into following
var result = {...state};
result.ingredients = [...state.ingredients, action.payload];
Note: In case there is a nested object in state or an object in array, they will still continue to share the same reference.
When used inside an object think of it as object.assign. the original state is added to object, then the next thing (ingredients), overwriting anything already there if necessary (ingredients), and so on down the line

Assign, and set js object property conditionally [duplicate]

This question already has answers here:
In JavaScript, how to conditionally add a member to an object?
(29 answers)
Conditionally set an object property
(7 answers)
How to conditionally add properties to a javascript object literal
(8 answers)
Closed 8 months ago.
Here's my code:
app.post('/ujfeladat', (req, res) => {
const {nev, tipus, szid} = req.body;
const hianyos = () => {
if(tipus === 'hianyos'){
return {rang: -1}
}
return
}
db('fl').insert({
nev: nev,
tipus: tipus,
szid: szid,
hianyos() //returns an error
}).returning('*')
.then(data => res.json(data))
.catch(err => console.log(err))
})
How can i do that to add the rang property to the object only if the tipus === 'hianyos'?
Updated answer:
Here's how you can do it:
// Will result in { foo: 'foo', bar: 'bar'}
const item = {
foo: 'foo',
... true && { bar: 'bar' },
... false && { falsy: 'falsy' },
}
console.log(item)
Explanations:
Short-circuit evaluation (true && {}, false && {}) would return an Object or a Boolean false value.
In the case an Object is returned, its properties get spread and assigned to the parent object.
In the case false value is returned, the parent object isn't polluted, because ES6 treats false, undefined, null and etc values as {}. Therefore spreading ...{} won't assign any properties to the parent object. More details about this, you can find here.
Original answer:
Here's how you can do it:
db('fl').insert({
nev: nev,
tipus: tipus,
szid: szid,
...tipus === 'hianyos' ? { rang: -1 } : {}
})
Explanations:
As you can see the ternary operator always returns an object.
If the condition is true, then it returns { rang: -1 }, otherwise an empty object {}.
After that we spread out ... the resulted object (from the ternary operation) and the object's properties are assigned to the parent object.
If there aren't any properties, then nothing will be assigned, which is our goal.
Code example: (sometimes few lines of code better than a thousands of words)
// Will result in { foo: 'foo', bar: 'bar'}
const item = {
foo: 'foo',
... true ? { bar: 'bar' } : {},
... false ? { falsy: 'falsy' } : {},
}
console.log(item)
In other answer I explained the same idea, but for arrays. You can check it too here.

Is there a nice way in javascript to removing Falsy values from a javascript object (not an array)?

In JavaScript you have the nice .filter method to remove null or falsy values from arrays. So far I haven't been able to find a method to remove the same from JavaScript Objects.
Why would this be?
Currently you can create a function for arrays like :
function stripNulls(arr) {
return arr.filter(Boolean);
}
Is there a similar function that can be created for JS Objects, or is the way filter works not practical on JS Objects.
The answer to "can I do x to an object" (or an array for that matter) is usually "yes" and it frequently involves some form of reduce.
If you want to filter falsy values you could do something like this:
function filterFalsy(obj) {
return Object.keys(obj).reduce((acc, key) => {
if (obj[key]) {
acc[key] = obj[key]
}
return acc
}, {})
}
const testObj = {
a: 'test',
b: 321,
c: false
}
console.log(filterFalsy(testObj))
This returns a new object without falsy values and leaves the existing object alone.
WARNING: There are better answers provided here. Also, thanks to comments made below user's should be warned using delete may provide suboptimal performance.
Filtering invalid values is a little more complex in objects. At face value this will do what you want:
var arr = [ 'apple', 43, false ];
var trueArr = arr.filter(Boolean);
console.log(trueArr);
var obj = { 'title': 'apple', 'id': 43, 'isOrange': false, 'test': 'asd' };
Object.keys(obj)
.filter(key => !obj[key])
.forEach(key => delete obj[key]);
console.log(obj);
However, this will not iterate over child objects / functions. This logic also directly modifies the original object (which may or may not be desired).
That can easily changed by adding this logic to a function like so:
function removeFalseyProperties(obj) {
Object.keys(obj)
.filter(key => !obj[key])
.forEach(key => delete obj[key]);
return obj;
}
var testObj = { 'title': 'apple', 'id': 43, 'isOrange': false, 'test': 'asd' };
var trutheyObj = removeFalseyProperties(testObj);
console.log(trutheyObj);
falsy values are 0, undefined, null, false, etc.
myArray
.map(item => {
// ...
})
// Get rid of bad values
.filter(Boolean);
By passing Boolean we can remove all the falsy values.

Categories

Resources