Updating object in nested array, react-redux - reactjs

I'm trying to update the objects that is in the nested array, below is an example of my state. I am trying to update the objects within goals I succeed with updating the objects.
BUT
Each time I update any object. the object at index 0, will get a copy of all the objects. And the more times I update, it creates more copies and they become nested within the object at index 0.
The object at index 0 will also be updated with the most recent update of any object.
{
list: {
'0': {
id: 0,
dueDate: 'By May 28th I Will have: ',
goals: [
{
0: {...}
1: {...}
3: {...}
}
]
}
'1':{
id: 0,
dueDate: 'By June 31st I Will have: ',
goals: [
{
2: {...}
4: {...}
}
}
keyName = index of object in list. ( the two ones above '0' and '1' : {
)
Reducer
return {
...state,
[action.payload.keyName]: {
...state[action.payload.keyName],
goals: [
{ ...state[action.payload.keyName].goals, ...action.payload.goal },
...state[action.payload.keyName].goals.slice(1, state[action.payload.keyName].goals.length)
]
}
};
Also if you know any good documentation or tutorial on normalizr please let me know.
Thank you in advance! :)

This will update a goal based in its keys, assuming a goal has unique keys.
const state = {
'0': {
id: 0,
dueDate: 'By May 28th I Will have: ',
goals: [
{a: 1,
b: 1,
c: 1}
]
},
'1':{
id: 0,
dueDate: 'By June 31st I Will have: ',
goals: [
{d: 1,
r: 1}
]
}
};
function reducer(state, keyName = 0, goal) {
const goals = [...state[keyName].goals];
const index = state[keyName].goals.findIndex((e) => Object.keys(e).every((key) => Object.keys(goal).includes(key)));
goals.splice(index,1, goal);
return {
...state,
[keyName]: {
...state[keyName],
goals,
}
};
}
console.log(reducer(state, 0, {a:3, b:2, c:4}));
This is assuming that you are updating your goals by array positioning.
const state = {
'0': {
id: 0,
dueDate: 'By May 28th I Will have: ',
goals: [
{test: 1},
{test: 1},
{test: 1}
]
},
'1':{
id: 0,
dueDate: 'By June 31st I Will have: ',
goals: [
{test: 1},
{test: 1}
]
}
};
function reducer(state, keyName = 0, goal) {
return {
...state,
[keyName]: {
...state[keyName],
goals: [{...state[keyName].goals, ...goal}]
}
};
}
console.log(reducer(state, 0, [{test:3}, {test:44}]));

Johan looks like you desctucture your state in a wrong way.
First, try to update your goals using array desctucturing goals: [{...state[keyName].goals, ...newGoal}]
And also maybe this one might come useful https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#updating-nested-objects

Related

How to push data into an same object based on specific key in typescript

I have an jason result as below and I want to read from it and push to specific key as below
JSON result
[
{id:1,Name:"test",billNumber:"123"}
{id:2,Name:"test1",billNumber:"123"}
{id:3,Name:"test2",billNumber:"12345"}
{id:2,Name:"test3",billNumber:"12345"}
{id:3,Name:"test4",billNumber:"12334535"}
]
I want to have array list as below
{
"123":[{id:1,Name:"test",billNumber:"123"}, {id:2,Name:"test1",billNumber:"123"}],
"12345":[ {id:3,Name:"test2",billNumber:"12345"},{id:2,Name:"test3",billNumber:"12345"}],
"12334535":[{id:3,Name:"test4",billNumber:"12334535"}]
}
How to get the above list from the json result. Please do help
You don't need lodash to do that: just a regular Array.prototype.reduce will do the work. At each iteration, you simply check if the billNumber of the current item is in the object:
if it is not (i.e. a new entry), then you assign an array with a single element
if it is (i.e. the billNumber has been encountered before), then you simply push into the array
See proof-of-concept below:
const data = [{
id: 1,
Name: "test",
billNumber: "123"
}, {
id: 2,
Name: "test1",
billNumber: "123"
}, {
id: 3,
Name: "test2",
billNumber: "12345"
}, {
id: 2,
Name: "test3",
billNumber: "12345"
}, {
id: 3,
Name: "test4",
billNumber: "12334535"
}];
const transformedData = data.reduce((acc, cur) => {
if (cur.billNumber in acc) {
acc[cur.billNumber].push(cur);
} else {
acc[cur.billNumber] = [cur];
}
return acc;
}, {});
console.log(transformedData);
Use groupBy from lodash:
const result = groupBy(input, "billNumber")
(input is your array)
you can use reduce method.
[
{id:1,Name:"test",billNumber:"123"},
{id:2,Name:"test1",billNumber:"123"},
{id:3,Name:"test2",billNumber:"12345"},
{id:2,Name:"test3",billNumber:"12345"},
{id:3,Name:"test4",billNumber:"12334535"},
].reduce((acc, value) => {
if (!acc[value.billNumber]) {
acc[value.billNumber] = [];
}
acc[value.billNumber].push(value);
return acc;
}, {})
Here is the mimic code You cann use and get help
var a = [{a:2},{a:3},{a:4}]
let b = {}
let c = 1
a.forEach(obj => {
b[c] = [obj]
c++
})
output will be
{
1: [ { a: 2 } ],
2: [ { a: 3 } ],
3: [ { a: 4 } ]
}
Thanks I hope it will help !

How can I update the state of objects within an array using React?

What is the best practice in React to push a new object to an array within an object that doesn't have a key and then have the state update? I'm currently able to update it, but it's mutating the state which I know is not a best practice. I'd like to be able to use this.setState so my component re-renders. Thanks!
currentState = {
lorem: [
{
id: 123,
title: 'quis nostrud'
ipsum: [
{
dolor: 0,
amet: 0
}
]
},
{
id: 456,
title: 'occaecat cupidatat'
ipsum: [
{
dolor: 0,
amet: 0
}
]
}
]
}
desiredState = {
lorem: [
{
id: 123,
title: 'quis nostrud'
ipsum: [
{
dolor: 0,
amet: 0
},
{
dolor: 100,
amet: 100
}
]
},
{
id: 456,
title: 'occaecat cupidatat'
ipsum: [
{
dolor: 0,
amet: 0
}
]
}
]
}
Yes you should not mutate the original state directly. You need to find the id of which object you need to update in the array then update the state.
Like this
const updatedLorem = currentState.lorem.map(x => {
if(x.id === '123') // <----- you will need to know which id to update
return [...x.ipsum, {dolor: 0, amet: 0}]
else
return x
})
this.setState(prev => ({
...prev,
lorem: updatedLorem
}))
I have updated the code from #gdh above which was overwriting the lorem object with the new ipsum array.
Here is a working example that instead appends the new object to the correct ipsum array within the lorem object: https://codesandbox.io/s/cool-star-k8kpo?fontsize=14&hidenavigation=1&theme=dark

Move an element from one array to another within same document MongoDB

I have data that looks like this:
{
"_id": ObjectId("4d525ab2924f0000000022ad"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 0, other: 235 },
{ id: 3, other: 765 }
],
"zeroes": []
}
I'm would like to to $pull an element from one array and $push it to a second array within the same document to result in something that looks like this:
{
"_id": ObjectId("id"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 3, other: 765 }
],
"zeroes": [
{ id: 0, other: 235 }
]
}
I realize that I can do this by doing a find and then an update, i.e.
db.foo.findOne({"_id": param._id})
.then((doc)=>{
db.foo.update(
{
"_id": param._id
},
{
"$pull": {"array": {id: 0}},
"$push": {"zeroes": {doc.array[2]} }
}
)
})
I was wondering if there's an atomic function that I can do this with.
Something like,
db.foo.update({"_id": param._id}, {"$move": [{"array": {id: 0}}, {"zeroes": 1}]}
Found this post that generously provided the data I used, but the question remains unsolved after 4 years. Has a solution to this been crafted in the past 4 years?
Move elements from $pull to another array
There is no $move in MongoDB. That being said, the easiest solution is a 2 phase approach:
Query the document
Craft the update with a $pull and $push/$addToSet
The important part here, to make sure everything is idempotent, is to include the original array document in the query for the update.
Given a document of the following form:
{
_id: "foo",
arrayField: [
{
a: 1,
b: 1
},
{
a: 2,
b: 1
}
]
}
Lets say you want to move { a: 1, b: 1 } to a different field, maybe called someOtherArrayField, you would want to do something like.
var doc = db.col.findOne({_id: "foo"});
var arrayDocToMove = doc.arrayField[0];
db.col.update({_id: "foo", arrayField: { $elemMatch: arrayDocToMove} }, { $pull: { arrayField: arrayDocToMove }, $addToSet: { someOtherArrayField: arrayDocToMove } })
The reason we use the $elemMatch is to be sure the field we are about to remove from the array hasn't changed since we first queried the document. When coupled with a $pull it also isn't strictly necessary, but I am typically overly cautious in these situations. If there is no parallelism in your application, and you only have one application instance, it isn't strictly necessary.
Now when we check the resulting document, we get:
db.col.findOne()
{
"_id" : "foo",
"arrayField" : [
{
"a" : 2,
"b" : 1
}
],
"someOtherArrayField" : [
{
"a" : 1,
"b" : 1
}
]
}

How to access value in object in array Mongodb findAndModify

How do I update a value inside a object inside an array. I will provide the exact array number in a variable... Heres my code:
var num = 0;
var ObjectID=require('mongodb').ObjectID;
db.collection('polls').findAndModify({
query: {_id: ObjectID(_id param)},
update: { $inc: { total: 1, "Data.chart." + num.toString + ".value": 1} }
});
This is what I want to update
{total: 0, "Data": [
{ value: 0, label: 'Beatles', color: '#4169E1' },
{ value: 0, label: 'Sting', color: '#C0C0C0' },
{ value: 0, label: 'Police', color: '#FFA500' },
{ value: 0, label: 'Journey', color: '#FF4500' },
{ value: 0, label: 'Genesis', color: '#EE82EE' }
]
}
You seem to have a subsection, chart in the query that does not exist in the actual data to update. Just eliminate that;
db.collection('polls').findAndModify({
query: {_id: ObjectID(_id param)},
update: { $inc: { total: 1, "Data." + num.toString + ".value": 1} }
});
turns the query into
db.polls.update({}, { $inc: { total: 1, "Data.2.value": 1} })
resulting in
db.polls.find().pretty()
{
"_id" : ObjectId("56cbebb6ef788d178e2dfdc0"),
"total" : 1,
"Data" : [
...
{
"value" : 1,
"label" : "Police",
"color" : "#FFA500"
},
...
JavaScript "stringifies" presented keys, so this is why you are not getting what you expect.
To use a variable as part of a "calculated key name" you need to use the bracket [] notation to define the object:
var num = 0;
var update = { "$inc": { "total": 1 } };
update["$inc"]["Data." + num + ".value"] = 1;
db.collection('polls').findAndModify({
query: { _id: ObjectID(_id param) },
update: update
});
Better yet, you should not presume the "index" that you think you want to modify is the actual location of the data. Instead match the array element in the query and use the positional $ operator instead of a hard index:
db.collection('polls').findAndModify({
query: { "_id": ObjectID(_idParam), "Data.label": 'Beatles' },
update: { "$inc": { "total": 1, "Data.$.value": 1 } }
});
So the element that matches Data.label as "Beatles" will be the one updated, and regardless of the current index position in the array.
That means "scalable", where other writes could alter the array content but you are still updating the one you want, even if it changed position due to another update.
N.B The document in your question has the path to array elements as Data and not Data.chart. But apply however your real data is contructed.

How to transform an array into a subset using only jsonpath?

I'd like to use jsonpath to transform an array with 3 elements to just 2 elements.
Given this object:
var _base = {
myArray: [{ item: 1, value: "first" }, { item: 2, value: "second" }, { item: 3, value: "third" }]
}
I'd like to use jsonpath-object transform to transform the object into:
var _newBase = {
myArray: [{ newItem: 2, newValue: "second" }, { newItem: 3, newValue: "third" }]
}
I understand I could do this with a simple slice call and some object manipulation, but I have complex, variable objects that I'm transforming at runtime, so jsonpath is the requirement.
I'm able to use the below:
var transform = require('jsonpath-object-transform');
var template = { ["$.myArray", { "newItem": "$..item", "newValue": "$..value" }] }
transform(_base, _template) //emits the below
Which emits:
[{ newItem: 1, newValue: "one" }, { newItem: 2, newValue: "second" }, { newItem: 3, newValue: "third" }]
But what I really need is the above object without its first index (so just the last two objects in the array).
You can use the #path syntax to ignore the first item:
var _template = {
foo: ['$.myArray[?(#path !== "$[\'myArray\'][0]")]']
};
Returns:
{ foo: [ { item: 2, value: 'second' }, { item: 3, value: 'third' } ] }
However, it seems that jsonpath-object transform currently doesn't support mixing both #path and accessing subfields like in {["$.data", {"key": "$.value"}]}.
So this leaves you with the following options:
You can acheive what you need by using two transform operations: the first to prune out the first item, and the second to rename subfields.
slice-ing your objects beforehand, as you suggested.
And finally, patching the library (and submitting a pull request while you're at it!). It's probably a quick fix anyway, shouldn't be too time-consuming.

Resources