I read the official Vuex example of how to mutate the state of element in array of objects data.
editTodo (state, { todo, text = todo.text, done = todo.done }) {
const index = state.todos.indexOf(todo)
state.todos.splice(index, 1, {
...todo,
text,
done
})
}
It seems a bit an overkill to find the index first (first loop), perform a splice and perform a copy with spread operator ...todo (would it be considered a second loop?).
Going by the docs:
When adding new properties to an Object, you should either:
Use Vue.set(obj, 'newProp', 123), or Replace that Object with a fresh one. For example, using the object spread syntax: state.obj = { ...state.obj, newProp: 123 }
There are only two ways to set / update properties to object.
In bigger data scenario that is causing some performance issues. Is there any better (more efficient) way to update an element in array of objects?
I am working with a Map data structure and I need to make an exact copy of the existing Map object without doubling memory usage.
If I were to use an array or a regular object instead of Map, shallow copies can be used to accomplish this via the spread operator which would allow me to reference the original object in memory and use additional memory as needed (only when any changes are made to the new object).
This is the desired behavior, only with a Map object.
This is what I tried:
const shallowCopy = {
storage: Object.assign({}, this.database.storage), //copying Map object
counts: { ...this.database.counts },
};
The above approach seems to result in an empty object and not successfully copy Map over.
Again, the main objective here is to create the equivalent of what would be a shallow copy with a regular object, let foo = {...bar}, only I'm using a Map object... after the copy I should be able to call the regular Map functions on the copy.. not sure if this is even possible.
I appreciate any suggestions, thanks in advance
You can use the map.prototype.entries() method and pass it to the Map constructor to get a shallow copy of the Map.
The Map.prototype.entries would return an iterator with the key and value in an array container:
//Sample Data
const childData = {
name: "john",
surname: "doe"
};
const data = {
foo: "bar",
baz: "foo",
child: childData
};
//Constructing Map from Object
const map = new Map(Object.entries(data));
//Shallow copying the map object to a new Map object
const shallowCopyMap = new Map(map.entries());
printMapAsJson(shallowCopyMap);
//Modifying the object ref
childData.name = "jane";
//Change is reflected as it is a shallow copy
printMapAsJson(shallowCopyMap);
function printMapAsJson(map) {
console.log(Object.fromEntries([...map.entries()]));
}
I have this small function (within my Angular 7 application) which uses JavaScript reduce(), and locates an object within a nested array of objects. I can then proceed to update certain properties on the fly.
Now, in addition to this find logic, I would like to also insert/delete an object into/from the nested array.
Question is: once I do locate my object, can I push() and/or delete an object ?
const input={UID:2,GUID:"",LocationName:"USA",ParentLocation:null,subs:[{UID:42,GUID:"",LocationName:"New Jersey",Description:"",subs:[{UID:3,GUID:"",LocationName:"Essex County",ParentLocation:null,"subs":[{UID:4,LocationName:"Newark",ParentLocation:3,"subs":[{"UID":49,"GUID":"","LocationName":"Doctor Smith's Office","LocationType":{"UID":2,"LocationTypeName":"Practice","Description":"other location"},"subs":[{"HostID":38,"HostName":"Ocean Host",}]}]}]}]}]};
const findUIDObj = (uid, parent) => {
const { UID, subs } = parent;
if (UID === uid) {
const { subs, ...rest } = parent;
return rest;
}
if (subs) return subs.reduce((found, child) => found || findUIDObj(uid, child), null);
};
console.log(findUIDObj(49, input));
var obj = findUIDObj(49, input);
delete obj;
For example, in my Angular 7 app, it complains if I attempt to delete the found object:
ex/
var obj = findUIDObj(49, input);
delete obj;
'delete' cannot be called on an identifier in strict mode.
Looking briefly at your code, I see you are using a const identifier to declare your data collection. We only use const for static data that does not change, and that’s the purpose of it. So, first of all matters, that seems to be the problem. To test it change it to let. Now, as for methods for data management, immutability is worthy of your consideration for many reasons, but namely Angular will rerender the entire object regardless of altering the existing object, or receiveing a new object. You can look up Immutable JavaScript to understand more. In many cases, creating immutable data management is done with a library, you can do it yourself. Basically, create a function called copy( data ), or something so that you pass in the original object, but you get a copy of it in return with no reference to the original object. That way one does not accidentally change the original object. To do this you can do this inside of your copy function: return JSON.parse(JSON.stringify( data )) ;
The only problem you might run into here is deep nested objects, or objects with circular references can cause problems. I have an overriding stringify method to mange this in little libraries I’ve written.
delete obj would never do what you want: first of all, it is not even an object from your input, since the function created a new object from the found object, excluding the subs property, and returned that. But more importantly, delete is used for deleting properties, not objects.
It seems you want to remove a matching object from its parent subs property. For that you would need to mutate the subs array, so it would exclude the matching object. For that to work in a generic way, your input should be an array. Otherwise that root object could not be removed from anything.
With that in mind, your lookup function should return the array in which the match was found and at which index. With those pieces of information you can decide to remove that element from the array, or to insert another object at that index.
Here is how it could work with removal:
const input=[{UID:2,GUID:"",LocationName:"USA",ParentLocation:null,subs:[{UID:42,GUID:"",LocationName:"New Jersey",Description:"",subs:[{UID:3,GUID:"",LocationName:"Essex County",ParentLocation:null,"subs":[{UID:4,LocationName:"Newark",ParentLocation:3,"subs":[{"UID":49,"GUID":"","LocationName":"Doctor Smith's Office","LocationType":{"UID":2,"LocationTypeName":"Practice","Description":"other location"},"subs":[{"HostID":38,"HostName":"Ocean Host",}]}]}]}]}]}];
const findUIDObj = (uid, arr) => {
if (!arr) return;
const idx = arr.findIndex(obj => obj.UID === uid);
if (idx > -1) return [arr, idx];
for (const obj of arr) {
const result = findUIDObj(uid, obj.subs);
if (result) return result;
}
};
console.log(findUIDObj(49, input));
const [arr, idx] = findUIDObj(49, input) || [];
if (arr) {
arr.splice(idx, 1); // Remove object from its parent array
}
Am i Mutating redux state with this function ?
function addDefinition(object, payload) {
var newObject = Object.assign({}, object);
if(!newObject[payload.guid]){
newObject[payload.guid] = [];
}
newObject[payload.guid].push(payload);
return newObject;
}
I want to create structure to hold data related to different part of application.
Yes, that .push() statement is mutating in the later calls. In the first call, the array is newly created, so it doesn't qualify as a mutation. In a later call, you've created a new object but copied the existing array reference, so .push() mutates the existing array.
Yes. See markerikson's answer. Keep in mind you could avoid this with the spread operator.
const INITIAL_STATE = {
guid: []
}
function addDefinition(state = INITIAL_STATE, payload) {
return {...state, guid: [...state.guid, payload]};
}
In addition you could avoid the if statement with initial state.
I have some inherited typescript code that looks like this (inside of a function):
const Statuses = [...this.Statuses]
this.Statuses refers to an array of status codes (defined as Statuses = status[];) defined inside the same .ts file. Subsequent to the line above, the code operates on Statuses
I'm trying to understand what the [...this.var] syntax does? Why not simply refer to this.Statuses in the first place?
Yes, I'm new to javascript/typescript....
Thanks in advance.
It's "spread notation", it spreads out this.Statuses (which must be some kind of Iterable, such as an array) into discrete elements. In this case, those form a new array (because the ...this.Statuses is within [], which creates an array).
So if this.Statuses is an array, it's functionally identical to const Statuses = this.Statuses.slice(); (or also the following).
If this.Statuses is another kind of Iterable, it's functionally identical to const Statuses = Array.from(this.Statuses);.
It's a aesthetically-pleasing way of copying an array, to avoid mutations to the original. It uses the spread syntax, as mentioned in other answers.
const a = [ 1 ]
const b = a
const c = [...a]
b.push(2)
c.push(3)
console.log(` original: ${a}`)
console.log(`reference: ${b}`)
console.log(` copy: ${c}`)
It's spread syntax. It allows to copy items from one array (or object) to another. In your case it's used to make a shallow copy of this.Statuses to avoid original array mutations. Thanks for this when you for example push new item to Statuses the original array (this.Statuses) will remain unchanged.