How to duplicate object in react - reactjs

Even replicating the object succeeded. However, the id value is duplicated the same. Therefore, if you click the delete button, the deletion will be done together.But I want to get a different ID. How can I fix it here?
const handleduplicate = id => {
const dupDataGroup = [...Groupdata].filter(item => item.id === id);
const newGroup = [dupDataGroup[0], ...Groupdata];
setGroupData(newGroup);
};
The result when I didn't do anything =>
0: {id: 0, title: 'blur', subtitle: 'Img'}
1: {id: 1, title: 'blurs', subtitle: 'Ima'}
2: {id: 2, title: 'Free ', subtitle: 'Ima'}
handleduplicate click result=>
0: {**id: 0**, title: 'blur', subtitle: 'Img'}
1: {**id: 0,** title: 'blur', subtitle: 'Img'}
2: {id: 1, title: 'blurs', subtitle: 'Ima'}
3: {id: 2, title: 'Free ', subtitle: 'Ima'}
I hope that only the ID value will change when duplicated.

React does not automatically update the key index in JSON objects. Also, in this scenario, you're fetching the value and inserting it into the newGroup.
If you really want to update the keys, I'd suggest creating a for loop to update the values of id.
const handleduplicate = id => {
const dupDataGroup = [...Groupdata].filter(item => item.id === id);
const newGroup = [dupDataGroup[0], ...Groupdata];
for (var i = 0; i < newGroup.length; i++)
newGroup[i].id = i;
setGroupData(newGroup);
};

You can duplicate an object by using the spread operator.
const obj = {name: 'ritik', surname: 'banger'};
const objCopy = {...obj};
or use of Object.assign will work for you.
let objcopy = Object.assign({}, obj);
You must note that here we are referencing the original object 'obj' and thus any changes in 'objcopy' will reflect in 'obj', this is known as shallow copy.
If you want to deep copy the object and you wish to have different reference for both then,
Using JQuery library:
import jQ from "jquery";
let objcopy = jQ.extend(true, obj, {});
Using lodash library:
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

[...Groupdata] creates a new list with the (non-duplicated) items in Groupdata. This is important if you'd want to add the item twice to the list, as changing one with change the same object as you already noticed. You will also have to duplicate the item itself.
Note that duplicating the list itself isn't necessary if you're just going to filter on it.
What I also think is happening, given the additional information in the comments, is that setGroupData is used multiple times. If you use Groupdata, followed by setGroupData, then Groupdata is not updated accordingly. So just make sure you use that function once.
const handleduplicate = (groupData, id, newId) => {
const dupDataGroup = groupData.filter(item => item.id === id); // no copying here.
const rest = groupData.filter(item => item.id !== id);
const newGroup = [
{ // the item is duplicated (note: no deep copy)
...dupDataGroup[0],
id: newId // updated with the new identifier
},
...rest
];
return newGroup;
};
const duplicated = handleduplicate(Groupdata, 123, 456);
setGroupData(duplicated);

Related

Subscribe to an observable and put values into an array

I'm new in angular and I need some help.
I have an observable getting users of type User[]
User: [
id: string,
name: string
]
and I have another array Ids of type string getting the ids of the selected users from a mat-select
Ids = this.Form.controls['users'].value
what I need right now is to subscribe to users$ observable, and get only the users that they have an id in Ids
const selectedUsers = ids.forEach(id =>this.usersSub$.value.filter((user) => user.userId === id))
something like the above but it is not really the right thing to do because it returns undefined . I'm wondering how should I properly get my selectedUsers array.
You use combineLatest to merge both observables and map all elements to accomplish it.
First, Create an observable with ids.
selectedIds$ = of([1, 3]);
players$ = of([
{ id: 1, name: 'lebron' },
{ id: 2, name: 'irving' },
{ id: 3, name: 'love' },
]);
Next, combine both observables, using the combineLatest operator, and return the players using the map to iterate over the response from the combineLast, use the filter and find to match the playerid with the ids from the selectedIds array.
const seletedUsers$ = combineLatest([this.selectedIds$,
this.players$])
.pipe(
map(([ids, players]) => {
return players.filter((p) => ids.find((id) => id === p.id));
})
)
.subscribe((v) => {
console.log(v);
});
https://rxjs.dev/api/index/function/combineLatest
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

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 convert an array of dictionaries into an array of keys using react

Given a list:
let names = [{name: "bobby"}, {name: "sydney"}, {name: "Paul"}, {name: "Grace"}
I want the output to be ["bobby", "sydney", "Paul", "Grace"]
Here is what I have tried:
var items = Object.keys(names).map(function(i) {
return names[i];
})
const items = Object.keys(names).map((key)=>names[key]);
this.setState({items});
console.log(this.state.items);
names.map(({ name }) => name)
const names = [{
name: "bobby"
}, {
name: "sydney"
}, {
name: "Paul"
}, {
name: "Grace"
}];
const keys = names.map(({
name
}) => name);
console.log(keys);
A note about react keys, they should be unique within the rendered siblings, i.e. they should be unique within the dataset. Names alone may not provide sufficient uniqueness.
A second note, you might not want to generate your react keys separately from where you need them, i.e. generally they are created when you are mapping JSX.
This is not really related to React. You can do that with JavaScript, for instance using API like map().
Here is an example:
let arr = names.map(obj => obj.name);

Find object in state and update property

I have a problem with a dynamic state I am setting. My first state looks like this:
const [exercises, setExercises] = useState([{
id: 123,
title: "Title here",
category: "someCategory"
}])
A user then selects an item of this state. I create a second state representing the selected object, but adding additional properties to it. For instance I am adding and initializing the properties 'amount' and 'unit'.
const [selectedExercises, setSelectedExercises] = useState([{
id: 123,
title: "Title here",
category: "someCategory",
amount: 0,
unit: ''
}])
I want the user to choose amount and unit from a form. How do I access and change those two properties in the state? Since I don't know the user's selection, I have to find the object within the state first.
I have tried things like (el being called from an input element somewhere):
setSelectedExercises([
...selectedExercises,
(selectedExercises.find(exercise => exercise.title === el.title).amount = 1),
])
How do I find the object in question and update its amount property (for example in an onChange method)?
const [selectedExercises, setSelectedExercises] = useState([{
id: 123,
title: "Title here",
category: "someCategory",
amount: 0,
unit: ''
}]);
// Your handler should look like this and
// you should call handleAmountChange(el.id, 1)
function handleAmountChange(amount, id) {
setSelectedExercises(prev => prev.map(selectedExercise => {
if (selectedExercise.id === id) {
return {
...selectedExercise,
amount
}
}
return selectedExercise;
}));
}
A more generic function to change any property would look like this.
function handleChange(id, property, value) {
setSelectedExercises(prev => prev.map(selectedExercise => {
if (selectedExercise.id === id) {
return {
...selectedExercise,
[property]: value
}
}
return selectedExercise;
}));
}

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