Right way to partially update all items in an array with items from another array in redux reducer? - reactjs

In Redux, what's the best practice to update all items in an array with items from another array only with the fields that are common to the 2 arrays.
So for example :
billArrayInStore = [{id, amount, dueDate, summary}, ...]
newBillArray = [{id, amount, dueDate}, ...]
Update each bill (amount, dueDate) but keep the 'summary' field untouched.
Thank you :)

You can use Array.prototype.map
newBillArray = billArrayInStore.map(bill => ({
...bill,
amount: 0, // new amount
dueDate: '', // new dueDate
}))

For each bill object in billArrayInStore, you want to see if there is a corresponding bill object in newBillArray by comparing IDs. If you find a matching bill object, you then merge the two bills together into a new object. These new bill objects are stored in a new array to avoid mutating the original.
Since this solution involves transforming existing bill objects and storing them in a new array, it's a perfect use case for Array.prototype.map.
const updatedBills = billArrayInStore.map(bill => {
// For each existing bill, check to see if there is a corresponding
// new bill by comparing bill IDs.
const newBill = newBillArray.find(newBill => newBill.id === bill.id);
// If there is a new bill, merge the bills together using spread syntax.
if (newBill) {
// Order matters here, you want to spread the new bill last so it
// can override any properties in the current bill. If the current
// bill has properties that don't exist in the new bill, they won't
// be changed.
return { ...bill, ...newBill };
}
// If there isn't a corresponding new bill, the current bill should be
// returned unmodified.
return bill;
});
Here's a snippet with a working example.
const billArrayInStore = [
{ id: 1, amount: 1000, summary: 'Rent' },
{ id: 2, amount: 50, summary: 'Internet' },
{ id: 3, amount: 110, summary: 'Electric' }
];
const newBillArray = [
{ id: 2, amount: 40 },
{ id: 3, amount: 125 }
];
const updatedBills = billArrayInStore.map(bill => {
const newBill = newBillArray.find(newBill => newBill.id === bill.id);
if (newBill) {
return { ...bill, ...newBill };
}
return bill;
});
console.log(updatedBills);

Related

How to return objects that have matching value when comparing to a separate array

In my state I have an object called foodLog which holds all entries a user enters with one of the keys being foodSelectedKey and I'm trying to return all entries that have a matching value from that key with a different array called foodFilter.
However, this doesn't work and errors out saying foodLog.filter() isn't a function - I've looked this up and it's because it's an Object (I think). Any help would be greatly appreciated!
state = {
// log food is for the logged entries
foodLog: {},
// used for when filtering food entries
foodFilter: [],
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.filter((item) => {
return foodLog.foodsSelectedKey.map((food) => {
return foodFilter.includes(food);
});
});
};
I guess the reason behind the error Is not a function is that the object can not be looped. By that it means you can not iterate an object with differend variables inside, if it has no index to be iterated like an array. The same goes for map(), find() and similar functions which MUST be run with arrays - not objects.
As far as I understand you have an object named foodLog which has an array named foodsSelectedKey. We need to find intersected elements out of foodFilter with the array. This is what I came up with:
state = {
// log food is for the logged entries
foodLog: {
foodsSelectedKey: [
{ id: 1, name: "chicken" },
{ id: 2, name: "mashroom" }
]
},
// used for when filtering food entries
foodFilter: [
{ id: 1, name: "chicken" },
{ id: 2, name: "orange" }
]
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.foodsSelectedKey.filter((key) =>
{
for (let i=0; i<foodFilter.length;i++){
if(foodFilter[i].name===key.name)
return true
}
return false;
}
);
return matched;
};
The Output is filtered array, in this case, of one element only:
[{
id: 1
name: "chicken"
}]
In order to check the output - run console.log(findMatches()). Here is the CodeSandbox of the solution. (check console at right bottom)

Updating array of objects with timestamps

I have a data structure like this :
var records = [
{
firstName: "Akira",
id: "0543236543",
},
{
firstName: "Harry",
id: "0994372684",
},
{
firstName: "Sherlock",
id: "0487345643",
},
{
firstName: "Kristian",
id: "04873452342",
},
];
I want to create a function that would add the date property (adjusted for hours) to each of object in the array depending on the length of the array.
Here's my code so far
function updateObject(obj) {
let timeNow = new Date();
let addHours = (h) => timeNow.setHours(timeNow.getHours() + h);
const ids = obj.map((record) => record.id);
const lastNames = obj.map((record) => record.id);
const time = obj.map(
(record, index) => (record["time"] = new Date(addHours(index)))
);
return obj;
}
This returns:
[
{
firstName: 'Akira',
id: '0543236543',
time: 2021-05-18T12:25:59.366Z
},
{
firstName: 'Harry',
id: '0994372684',
time: 2021-05-18T13:25:59.366Z
},
{
firstName: 'Sherlock',
id: '0487345643',
time: 2021-05-18T15:25:59.366Z
},
{
firstName: 'Kristian',
id: '04873452342',
time: 2021-05-18T18:25:59.366Z
}
]
First issue: I want to update the time by adding 0,1,2,3 hours to the current time.
Currently it:
adds 0 to time and sets is a current time
adds 1 to updated time and set it a as a current time and so on.
Second:
I want to modify the function so it takes an add additional argument, "time increment" that if set to x will update the time property for first x records for 0 hours and the next x records for 1 hour.
Hope that makes sense :)
First issue can be solved by changing addHours to be a pure function that does not mutate timeNow:
const timeNow = Date.now();
const addHours = (h) => new Date(timeNow + h * 60 * 60 * 1000);
Second issue can be solved by looking at the index in the map:
const time = obj.map(
(record, index) => (record["time"] = new Date(addHours(index > increment ? 1 : 0))));
And in general, I would advise against mutating objects/arrays. Pure functions are usually easier to reason about and offer fewer surprises.
I would write the updateObject function so it does not mutate the original, but returns a new array with new objects.
Thanks! What if the increment parameter defines how many first x records should have the time property increased by 1h
For example my contacts array length is 4.
function updateObject(records, 1) {
\\code here
}
The first object would have the time property increased by 0h.
The second object would have the time property increased by 1h.
The third object would have the time property increased by 2h.
The fourth object would have the time property increased by 3h.
function updateObject(records, 3) {
\\code here
}
The first 3 objects would have the time property increased by 0h.
The fourth object would have the time property increased by 1h.
Do I need to slice the array into sub arrays first?

Devide list from database into multiple section (React)

I am currently struggling with this problem. Hopefully, you can help me :)
Data is selected from a Database and it returns Objects structured like this:
object = {
id: 4,
name: "Banana",
idParent: 1
}
idParent would be the section of the product.
There are a lot of products and a lot of sections so a simple
const sectionOne = [];
ObjectList.map(e => {
if(e.idParent === 1) {
sectionOne.push(e);
}
})
would probably be wrong, because it should be possible to add other idParents in the future and code should not need some rework in that case.
Let's say there are 30 Objects, 10 have idParent = 1, 15 have idParent = 2 and the last 5 have idParent = 3.
How can the whole list be divided into these sections without making a variable for each section?
Thanks for the help :)
What I believe you need here is a map which groups the values of the list by idParent.
Example:
const objectList = [{
id: 4,
name: "Banana",
idParent: 1
},
{
id: 3,
name: "apple",
idParent: 2
},
{
id: 5,
name: "orange",
idParent: 2
}];
const groupBy = (array, key) => {
return array.reduce((accumlator, value) => {
(accumlator[value[key]] = accumlator[value[key]] || []).push(value);
return accumlator;
}, new Map());
};
const resultMap = groupBy(objectList, "idParent");
console.log(resultMap);
enter code here
The sub-arrays from the map can be access also like this:
const groupWihtIdParen1 = resultMap[1];
// or like this
const groupWithIdParent2 = resultMap.get(2);
``

Having problem to desctructure my data on React

The upcomingWork contains my apis data. What I want here is to insert this as an array to display it on my calendar, below are the example of structured data I want.
Actually, the upcomingWork contains a lot of attributes but I just want to get those three attributes to display on my calendar.
const data = upcomingWork.map(u => u.id && u.title && u.created_at);
Example of array that I want to create from data.
const events = [
{
id: 1,
title: 'My event',
start: new Date(2020, 4, 8, 10, 0),
end: new Date(2020, 4, 8, 14, 0),
},
];
The map that you have will simply set every value to either true if all values are truey or false if any of them are falsey. if you want to extract those 3 values just change the map to this:
const data = upcomingWork.map(({id, title, created_at}) => ({id, title, created_at}))
Whatever you returned is not going to give you any desired data.
I think you misunderstood the map method of Array.
Instead you should use map like:
const data = upcomingWork.map(u => {
const {id, title, created_at} = u;
return {
id,
title,
created_at
}
});
const data = upcomingWork.map(u => {
return { id: u.id, title: u.title };
});
Your upcomingWork.map doesn't create an object at all, so it has no chance of creating the expected data. Instead, you can map appropriate properties of the u object to the new keys in a new object you create.
const events = upcomingWork.map(u => ({
id: u.id,
title: u.title,
start: u.created_at,
end: u.end //change the u.end to whatever property you have in your data
}));
(the new object has to be wrapped in () to ensure JS interprets it as a value, not a function body)

How to remove the right element from array react?

I want to remove an element from my array when click on a specific row.
When I click on an element it does nothing or the last row gets deleted.
I tried to remove the element like this:
ondeleterow(e: any) {
const array = [...this.state.fields.columns]; // make a separate copy of the array
const index = array.indexOf(e.target.id);
if (index !== -1) {
array.splice(index, 1);
this.setState({ fields: { columns: array }});
}
}
My array/json object looks like this:
[ {index: 0, name: "person 1", age: 12},
{index: 1, name: "person 2", age: 19},
{index: 2, name: "person 3", age: 34},
]
My result should be when I click on a row with ID=1 the row with index: 1 gets deleted from my state array.
I can't give them an Id because when I submit the json structure then does not get accepted.
I feel like your Array.splice might be causing the issue here (because even though you created a new array, the objects in the array are still passed by reference).
I would recommend a completely different method of doing this operation which I've found to be far cleaner and robust.
First you have to add a unique id field to each row. (this is good practice in react anyway, instead of using index for keys).
ondeleterow(id: string) {
return (e: any) => {
const array = this.state.fields.column.filter(item => item.id != id)
this.setState({ fields: { columns: array }});
}
}
and when you're mapping over your rows, you can simply add the function to the onClick like this
<Row key={item.id} onClick={ondeleterow(item.id)} />
Never use splice in react especially with state. They directly mutate the data. Use non mutating operations like slice.
Your code should as follows
ondeleterow(e: any) {
const array = [...this.state.fields.columns]; // make a separate copy of the array
const index = array.indexOf(e.target.id);
if (index !== -1) {
array.splice(index, 1);
this.setState({ fields: {
columns: [ ...array.slice(0, index), ...array.slice(index + 1, array.length) ]
}});
}
}
You can use Array.filter. This will allow you to create a new array with only the items you want based on a certain criteria. In this case, you want an array with items that have a different ID that the one you want to remove. So it will look like this
// Actual processing
const filterByIndex = (arr, idx) => arr.filter(x => x.index !== idx);
// Your data
const json = [{
index: 0,
name: "person 1",
age: 12
},
{
index: 1,
name: "person 2",
age: 19
},
{
index: 2,
name: "person 3",
age: 34
},
];
// Printing the result
console.log(filterByIndex(json, 1));
In your React app
ondeleterow(e: any) {
const columns = this.state.fields.columns.filter(x => x.index !== e.target.id);
this.setState({ fields: { columns }});
}
Try this
onDeleteRow(e) {
const afterRemoval = this.setState.fields.columns.filter(item => item.index != e.target.id);
this.setState(prevState => ({ fields: { ...prevState.fields, columns: afterRemoval } }));
}
The other solution above sets the fields field directly, It may work but will cause problem if fields has some other attribute other than columns (those attributes will get removed)

Resources