interface list {
name: string,
id: number,
marks: number
}
component state :
{
listOfStudents: list[],
}
now on some event, where marks is modified for a student with given name and following handler function, the following doesn't work
currentList = this.state.listOfStudents;
//find the list object with the given name
listToModify = currentList.map(item, ....=> item.name === event.name);
// update its marks
listToModify.marks = event.marks;
//set new state
this.setState({listOfStudents: currentList});
But the following works:
//get a copy of the object
currentList = [...this.state.listOfStudents];
//find the list object with the given name
listToModify = currentList.map(item, ....=> item.name === event.name);
// update its marks
listToModify.marks = event.marks;
//set new state
this.setState({listOfStudents: currentList});
I didn't have to add new data but modify an existing one, why is mutation required in such a case?
for a given state that is an object or array, react uses only its reference to compare and decide if it will update state, it doesn't do deep comparision between objects values.
the first case you are passing the same reference, therefore there is no update state. actually, if you modify the array at first example, that's actually mutating state directly.
second case you are cloning into a new fresh array, which holds a different reference. since the reference is different from previous array, react now update its state. that's actually the right approach to update state when dealing with arrays/objects.
Related
I have been trying to filter an Array by its props and not by its value so my original array would be -
const orignalArray =[
{id: 1, name:"jim", email:"jim#mail.com",age:20},
{id: 1, name:"jom", email:"jom#mail.com",age:30}
]
id like to be able to use (n) amount of filters.
My output array would look ideally look like this
const filterList["id","age"]
const newArray=[{name:"jim", email:"jim#mail.com"},{ name:"jom", email:"jom#mail.com"}]
I have tried to use filter() but cant seem to get it to work.
any help is much appreciated.
In this case you aren't filtering the array rather creating a new one based on the original with derived elements from each. To achieve this you can use the array map function to loop over the array and create a new one with new objects without the desired properties, e.g.:
function removeArrayElementProps(arr, propsToRemove) {
return arr.map((element) => {
// Create a copy of the original element to avoid modifying the original
const newElement = {...element};
// Remove the desired properties from the new element
propsToRemove.forEach((propToRemove) => {
delete newElement[propToRemove];
});
// Returning the modified element in map puts it in thew new arry
return newElement;
});
}
Then you simply call:
const newArr = removeArrayElementProps(orignalArray, ['id','age']);
This loops over the array, creates a copy of each element, and removes the specified properties from the element.
I use a MapStateDescriptor for my stateful computation. Some code here
final val myMap = new MapStateDescriptor[String, List[String]]("myMap", classOf[String], classOf[List[String]])
During my computation i want to update my map by adding new elements to the List[String].
Is it possible?
Update #1
have written following def to manage my map
def updateTagsMapState(mapKey: String, tagId: String, mapToUpdate: MapState[String, List[String]]): Unit = {
if (mapToUpdate.contains(mapKey)) {
val mapValues: List[String] = mapToUpdate.get(mapKey)
val updatedMapValues: List[String] = tagId :: mapValues
mapToUpdate.put(mapKey, updatedMapValues)
} else {
mapToUpdate.put(mapKey,List(tagId))
}
}
Sure, it is. Depending on whether this is Scala List or Java that You will be using there you can do smth like that to actually create the state from descriptor:
lazy val stateMap = getRuntimeContext.getMapState(myMap)
Then You can simply do:
val list = stateMap.get("someKey")
stateMap.put("someKey", list +: "SomeVal")
Note that if You would work with mutable data structure, You wouldn't necessarily need to call put again, since the update of the data structure would also update the state. But this approach does not work in case of RocksDB state, since the state is only updated after You call put in this case, so it is always advised to update the state itself instead of just underlying object.
How can ı equalize my array ıd and my value ıd and access value.name I didn't do it
This is my code:
activity(val) {
var act = this.items.map(function (val) {
if (element.ActivityID== val) {
return element.ActivityName
}
return act
});
Perhaps this?
activity (val) {
const activity = this.items.find(item => item.ActivityID === val)
return activity && activity.ActivityName
}
This just finds the item with the corresponding ActivityID and then returns its ActivityName.
Your original code contained several possible mistakes:
Two different things called val.
element doesn't appear to be defined.
The return act was inside the map callback. The activity method itself wasn't returning anything.
Not really clear why you were using map to find a single item. map is used to create a new array with the same length as the original array with each item in the new array determined by the equivalent item in the original array. It 'maps' the items of the input array to the items in the output array.
I have this code that I am testing on jsfiddle
onVote = (dir, index) => {
console.log(this.state)
const products = [...this.state.products]
products[index].votes = dir ? products[index].votes + 1 : products[index].votes - 1
console.log(this.state.products[index].votes)
// this.setState({products})
};
https://jsfiddle.net/hkL3wug7/2/
However, even though I am not setting State, the console log shows that the state is changes every time I click on the plus and minus signs.
I did the same as in this article https://medium.com/#giltayar/immutably-setting-a-value-in-a-js-array-or-how-an-array-is-also-an-object-55337f4d6702
const newState = [...state] // clone the array
newState[action.index].done = true
return newState
as far as I understand
(it is not duplicate of the other question, I am not asking for an efficient way)
As #Carcigenicate mentioned, you have created a shallow copy of the array which means you have a new array pointing to the same objects in the original.
To avoid mutating the original object, you will need to also create a copy of the one you would like to mutate, e.g.:
// Shallow copy of the array
const products = [...this.state.products];
// Shallow copy of the object within the array
const updatedProduct = { ...products[index] };
// Update the copy of the object
updatedProduct.votes = dir ? updatedProduct.votes + 1 : updatedProduct.votes - 1;
// Replace the object with the updated copy
products[index] = updatedProduct;
As #Carcigenicate mentioned in the comment, using the spread operator creates a shallow copy of the array. This is creating a problem for you because the expanded version of the array contains Objects which are passed by reference. So even though your local variable products is a new copy of this.state.products, they both contain references to the same Objects.
To achieve what you are trying to do, you would have to clone the objects in this.state.products. One possible way to do this is using Object.assign and replace your const products = [...this.state.products] with:
const products = [
Object.assign({}, this.state.products.Orange),
Object.assign({}, this.state.products.Apples),
Object.assign({}, this.state.products.Bananas)
]
So I'm using ngrx for managing the state in my application. I tried to add a new property (selected shifts) which should look like this:
state: {
shifts: {
selectedShifts: [
[employeeId]: [
[shiftId]: shift
]
]
}
}
at the moment, my state looks like this:
state: {
selectedShifts: {
[employeeId]: {
[shiftId]: shift
}
}
}
so as you can see, my "selected shift" is a property, not an array - which makes it diffictult to add/remove/query the state.
How do I compose the state to look like I want it?
This is what I tried in the reducer:
return {
...state,
selectedShifts: {
...state.selectedShifts,
[action.payload.employeeId]: {
...state.selectedShifts[action.payload.employeeId],
[action.payload.shiftId]: action.payload[shift.shiftId]
}
}
};
Now when I try to return the state in the way I'd like to, this is the result:
state: {
selectedShifts: {
[action.payload.employeeId]:
[0]: {[action.payload.shiftId]: { shift }}
}
}
What am I missing here? When I try to replace the {} items which should be [] this error comes up: "," expected.
Oh yea, I would like the index of the array to be the id of the specific shift and not [0], [1]...
Is this possible at all?
Would it be a bad idea to change the index from numerics to the actual shift's id?
Array length kind of miss behaves when you add data at numeric index points. This might get you into problems with array methods using length join, slice, indexOf etc. & array methods altering length push, splice, etc.
var fruits = [];
fruits.push('banana', 'apple', 'peach');
console.log(fruits.length); // 3
When setting a property on a JavaScript array when the property is a valid array index and that index is outside the current bounds of the array, the engine will update the array's length property accordingly:
fruits[5] = 'mango';
console.log(fruits[5]); // 'mango'
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 6
There is no problem selecting / updating state from object, it's just a bit different from what you're probably used to. With straight hashmap { objectId: Object } finding the required object to update / remove is the fastest possible if changes are defined for object id.
I know your problem is related to NGRX but reading Redux immutable patterns is going to definitely help you out here for add / update / remove objects from the state. https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
Generally you don't want to have arrays in state ( at least large arrays ) object hashmaps are a lot better.
To get array of your selected user shifts for views you could do something like. Note this is not a shift indexed array just array of shifts under userId property. From original state form following state.
state: {
selectedShifts: {
[employeeId]: {
[shiftId]: shift
}
}
}
const getSelectedShiftsAsArray = this.store.select( getSelectedShifts() )
.map(
userShifts => {
// get array of object ids
const userIds = Object.keys( userShifts );
const ret = {};
for( const userId of userIds ) {
const collectedShifts = [];
// convert Dictionary<Shift> into a Shift[]
// get array of shift ids
const shiftIds = Object.keys( userShifts[userId] );
// map array of shift ids into shift object array
collectedShifts = shiftIds.map( shiftId => userShifts[shiftId] );
// return value for a userId
ret[userId] = collectedShifts;
}
return ret;
});
Code is completely untested and just for a reference one level up from pseudocode. You could easily convert that into a NGRX selector though. The state is there just for the storage, how you model it for use in components is upto selector functions & components themselves.
If you really really need it you could add.
ret[userId].shiftIds = shiftIds;
ret[userId].shifts = collectedShifts;
But it really depends on how you plan to use these.
From my personal experience I would separate shift entities from selectedShifts but how you organise your state is completely up to you.
state: {
shifts: {
// contains shift entities as object property map id: entity
entities: Dictionary<Shift>,
selectedShifts: [
[employeeId]: number[] // contains ids for shifts
]
}
}
Now updating / removing and adding a shift would just be setting updated data into path shifts.entities[entityId]
Also selectedShifts for employeeId would be about checking if id is already in there and appending it into an array if it wasn't. ( If these arrays are humongous I'd go with object hash here too for fast access. <employeeId>: {shiftId:shiftId} ).
Check also:
redux: state as array of objects vs object keyed by id