Remove and Add items from array in nested javascript objectObject map in JavaScriptre-write javascript...
If a planet has 3 moons, is it possible to have triple Full/New Moons at once?
How would one muzzle a full grown polar bear in the 13th century?
How do I deal with a coworker that keeps asking to make small superficial changes to a report, and it is seriously triggering my anxiety?
What software provides a code editing environment on iPad?
Was there a shared-world project before "Thieves World"?
How to pronounce 'C++' in Spanish
Seemingly unused edef prior to an ifx mysteriously affects the outcome of the ifx. Why?
Phrase for the opposite of "foolproof"
How did Captain America manage to do this?
Binary Numbers Magic Trick
Does a semiconductor follow Ohm's law?
Apply MapThread to all but one variable
How do I reattach a shelf to the wall when it ripped out of the wall?
Is there an official tutorial for installing Ubuntu 18.04+ on a device with an SSD and an additional internal hard drive?
How much cash can I safely carry into the USA and avoid civil forfeiture?
Is it idiomatic to construct against `this`?
What term is being referred to with "reflected-sound-of-underground-spirits"?
How to stop co-workers from teasing me because I know Russian?
How to have a sharp product image?
Can SQL Server create collisions in system generated constraint names?
How to reduce LED flash rate (frequency)
Why does nature favour the Laplacian?
How does a program know if stdout is connected to a terminal or a pipe?
Was there a Viking Exchange as well as a Columbian one?
Remove and Add items from array in nested javascript object
Object map in JavaScriptre-write javascript arrayWrapping the classList API to add/remove array of classesSwitching from functional jQuery code to object-orientedJavaScript function to generate tree object from flat objectAdd functionality to object then remove those functions on exportAdd/remove class event handlerEasy Deep Copy of Array JavascriptAdding items to an array in Javascriptsplitting and merging Ranges array in javascript
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
$begingroup$
I have the following object at React state:
groups: [
{
id: 1,
items: [
{id: 1},
{id: 2},
{id: 3}
]
},
{
id: 3,
items: [
{id: 1},
{id: 3},
{id: 4}
]
}
]
I have written the following functions to add and remove items:
removeItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex= groups.findIndex(g=> g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.filter((i) => i.id !== itemId)
this.setState({groups})
}
addItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex = groups.findIndex(g => g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.concat({id: itemId})
this.setState({groups})
}
Is there any way to write it cleaner?
javascript object-oriented array
$endgroup$
add a comment |
$begingroup$
I have the following object at React state:
groups: [
{
id: 1,
items: [
{id: 1},
{id: 2},
{id: 3}
]
},
{
id: 3,
items: [
{id: 1},
{id: 3},
{id: 4}
]
}
]
I have written the following functions to add and remove items:
removeItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex= groups.findIndex(g=> g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.filter((i) => i.id !== itemId)
this.setState({groups})
}
addItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex = groups.findIndex(g => g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.concat({id: itemId})
this.setState({groups})
}
Is there any way to write it cleaner?
javascript object-oriented array
$endgroup$
$begingroup$
"Note this replicate of my code use 'group' and 'item' that its easier to read than my real code" Yes, in the lines of a Minimum Complete Verifiable Example. Which would've been commendable, if this was Stack Overflow. However, this is Code Review. Here, we require the real deal. The actual code, within it's actual context. Please include your actual code instead of a simplification. We don't deal well with those. Please consider reading our FAQ on how to get the best value out of Code Review when asking questions.
$endgroup$
– Mast
Apr 18 at 17:29
add a comment |
$begingroup$
I have the following object at React state:
groups: [
{
id: 1,
items: [
{id: 1},
{id: 2},
{id: 3}
]
},
{
id: 3,
items: [
{id: 1},
{id: 3},
{id: 4}
]
}
]
I have written the following functions to add and remove items:
removeItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex= groups.findIndex(g=> g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.filter((i) => i.id !== itemId)
this.setState({groups})
}
addItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex = groups.findIndex(g => g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.concat({id: itemId})
this.setState({groups})
}
Is there any way to write it cleaner?
javascript object-oriented array
$endgroup$
I have the following object at React state:
groups: [
{
id: 1,
items: [
{id: 1},
{id: 2},
{id: 3}
]
},
{
id: 3,
items: [
{id: 1},
{id: 3},
{id: 4}
]
}
]
I have written the following functions to add and remove items:
removeItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex= groups.findIndex(g=> g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.filter((i) => i.id !== itemId)
this.setState({groups})
}
addItem(groupId, itemId) {
// make a copy
let {groups} = this.state
const gIndex = groups.findIndex(g => g.id == groupId);
let items = groups[gIndex].items
groups[gIndex].items = items.concat({id: itemId})
this.setState({groups})
}
Is there any way to write it cleaner?
javascript object-oriented array
javascript object-oriented array
edited Apr 18 at 17:35
Rashomon
asked Apr 18 at 16:52
RashomonRashomon
1134
1134
$begingroup$
"Note this replicate of my code use 'group' and 'item' that its easier to read than my real code" Yes, in the lines of a Minimum Complete Verifiable Example. Which would've been commendable, if this was Stack Overflow. However, this is Code Review. Here, we require the real deal. The actual code, within it's actual context. Please include your actual code instead of a simplification. We don't deal well with those. Please consider reading our FAQ on how to get the best value out of Code Review when asking questions.
$endgroup$
– Mast
Apr 18 at 17:29
add a comment |
$begingroup$
"Note this replicate of my code use 'group' and 'item' that its easier to read than my real code" Yes, in the lines of a Minimum Complete Verifiable Example. Which would've been commendable, if this was Stack Overflow. However, this is Code Review. Here, we require the real deal. The actual code, within it's actual context. Please include your actual code instead of a simplification. We don't deal well with those. Please consider reading our FAQ on how to get the best value out of Code Review when asking questions.
$endgroup$
– Mast
Apr 18 at 17:29
$begingroup$
"Note this replicate of my code use 'group' and 'item' that its easier to read than my real code" Yes, in the lines of a Minimum Complete Verifiable Example. Which would've been commendable, if this was Stack Overflow. However, this is Code Review. Here, we require the real deal. The actual code, within it's actual context. Please include your actual code instead of a simplification. We don't deal well with those. Please consider reading our FAQ on how to get the best value out of Code Review when asking questions.
$endgroup$
– Mast
Apr 18 at 17:29
$begingroup$
"Note this replicate of my code use 'group' and 'item' that its easier to read than my real code" Yes, in the lines of a Minimum Complete Verifiable Example. Which would've been commendable, if this was Stack Overflow. However, this is Code Review. Here, we require the real deal. The actual code, within it's actual context. Please include your actual code instead of a simplification. We don't deal well with those. Please consider reading our FAQ on how to get the best value out of Code Review when asking questions.
$endgroup$
– Mast
Apr 18 at 17:29
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
Not a copy
// make a copy
let {groups} = this.state
You comment that the line following makes a copy. This is not the case, you are just creating a new reference named groups
to the array..
Use const
As you do not change the reference you should define groups
as a constant.
const {groups} = this.state;
// or
const groups = this.state.groups;
Array.push
rather than Array.concat
Array.concat
creates a new array, it is more efficient to just Array.push
a new item to the existing array
groups[gIndex].items = items.concat({id: itemId});
// can be
groups[gIndex].items.push({id: itemId});
Array.find
rather than Array.findIndex
You find the index of the group you want to modify. This complicates the code. If you use Array.find
it will return the group
and you don't need to index into groups
each time.
const gIndex= groups.findIndex(g=> g.id == groupId);
// becomes
const group = groups.find(group => group.id == groupId);
Common tasks in functions
Between the two functions you repeat some code. I would imaging that other functions also need to access groups
by id, make changes, and set the state of the new content. It would be best to provide functions to do that.
getGroupById(id) { return this.state.groups.find(group => group.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
Good naming
The functions addItem
and removeItem
do not indicate that they are related to adding/removing from a group. Better names could be removeItemFromGroup
, addItemToGroup
Be consistent
Good code style is consistent. Whether or not you use semicolons you should avoid doing it sometimes, do it always or never. (best option is use them)
Rewrite
Using the above points you could rewrite the code as the following.
Added two functions to do the common task of getting by group id and updating the state with groups.
I assume that the groupId
will exist as the code you have given indicates this to be so, if the groupId
does not exist your code would throw, and so would the following code.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
},
addItemToGroup(groupId, id) {
this.getGroupById(groupId).items.push({id});
this.updateGroups();
},
If items
are unique per group you can avoid the using Array.filter
and having to create a new array by splicing the item out of the array using Array.splice
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
const itemIdx = items.findIndex(item => item.id === id);
itemIdx > -1 && items.splice(itemIdx, 1);
this.updateGroups();
},
Or if items are not unique, or unique only sometimes you could iterate over the array removing them as you encounter them.
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
var i = items.length;
while (i--) { items[i].id === id && items.splice(i, 1) }
this.updateGroups();
},
Safer
The next example guards the function from trying to modify items if the group does not exist.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
}
},
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items.push({id});
this.updateGroups();
}
},
And just in case you don't want to duplicate items in a group.
// Only adds items if if it does not already exist in the group.
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group && !group.items.some(item => item.id === id)) {
group.items.push({id});
this.updateGroups();
}
},
$endgroup$
$begingroup$
Fantastic answer, thanks! However, I have just realizedthis.state.groups
should be treated as immutable (reactjs.org/docs/react-component.html#state). That means it should be cloned and then updated usingsetState()
strictly. This is a problem my original code has already... So maybe the final answer could be jsfiddle.net/tnug4v9L/1 ? (I had to remove some of your class methods, because the operations should be done in the clone instead of the class properties)
$endgroup$
– Rashomon
Apr 19 at 8:48
1
$begingroup$
@Rashomon Strictly would be enforced (the state data would be frozen and impossible to change) You are safe mutating the state because you set the state at the end of the function withthis.setState
The state can not be changed while your function is executing so there is no danger of losing state data. All the examples in my answer are safe to use as they are. As for the two extra class functions, that is up to you, but should does not mean must do, JavaScript is fully capable of enforcing such rules, so if you can do, then should do is only advisory.
$endgroup$
– Blindman67
Apr 19 at 13:06
$begingroup$
Thank you very much for your explanations. Im new to React so I will study carefully the behaviour of the state
$endgroup$
– Rashomon
Apr 19 at 13:10
add a comment |
$begingroup$
For the remove function, how about using a map and filter instead:
removeItem (groupId, itemId){
let {groups} = this.state;
groups = groups.map((group) => {
if(group.id === groupId){
group.item = group.item.filter(item => item.id !== itemId);
}
return group;
});
this.setState({groups});
}
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217682%2fremove-and-add-items-from-array-in-nested-javascript-object%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Not a copy
// make a copy
let {groups} = this.state
You comment that the line following makes a copy. This is not the case, you are just creating a new reference named groups
to the array..
Use const
As you do not change the reference you should define groups
as a constant.
const {groups} = this.state;
// or
const groups = this.state.groups;
Array.push
rather than Array.concat
Array.concat
creates a new array, it is more efficient to just Array.push
a new item to the existing array
groups[gIndex].items = items.concat({id: itemId});
// can be
groups[gIndex].items.push({id: itemId});
Array.find
rather than Array.findIndex
You find the index of the group you want to modify. This complicates the code. If you use Array.find
it will return the group
and you don't need to index into groups
each time.
const gIndex= groups.findIndex(g=> g.id == groupId);
// becomes
const group = groups.find(group => group.id == groupId);
Common tasks in functions
Between the two functions you repeat some code. I would imaging that other functions also need to access groups
by id, make changes, and set the state of the new content. It would be best to provide functions to do that.
getGroupById(id) { return this.state.groups.find(group => group.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
Good naming
The functions addItem
and removeItem
do not indicate that they are related to adding/removing from a group. Better names could be removeItemFromGroup
, addItemToGroup
Be consistent
Good code style is consistent. Whether or not you use semicolons you should avoid doing it sometimes, do it always or never. (best option is use them)
Rewrite
Using the above points you could rewrite the code as the following.
Added two functions to do the common task of getting by group id and updating the state with groups.
I assume that the groupId
will exist as the code you have given indicates this to be so, if the groupId
does not exist your code would throw, and so would the following code.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
},
addItemToGroup(groupId, id) {
this.getGroupById(groupId).items.push({id});
this.updateGroups();
},
If items
are unique per group you can avoid the using Array.filter
and having to create a new array by splicing the item out of the array using Array.splice
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
const itemIdx = items.findIndex(item => item.id === id);
itemIdx > -1 && items.splice(itemIdx, 1);
this.updateGroups();
},
Or if items are not unique, or unique only sometimes you could iterate over the array removing them as you encounter them.
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
var i = items.length;
while (i--) { items[i].id === id && items.splice(i, 1) }
this.updateGroups();
},
Safer
The next example guards the function from trying to modify items if the group does not exist.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
}
},
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items.push({id});
this.updateGroups();
}
},
And just in case you don't want to duplicate items in a group.
// Only adds items if if it does not already exist in the group.
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group && !group.items.some(item => item.id === id)) {
group.items.push({id});
this.updateGroups();
}
},
$endgroup$
$begingroup$
Fantastic answer, thanks! However, I have just realizedthis.state.groups
should be treated as immutable (reactjs.org/docs/react-component.html#state). That means it should be cloned and then updated usingsetState()
strictly. This is a problem my original code has already... So maybe the final answer could be jsfiddle.net/tnug4v9L/1 ? (I had to remove some of your class methods, because the operations should be done in the clone instead of the class properties)
$endgroup$
– Rashomon
Apr 19 at 8:48
1
$begingroup$
@Rashomon Strictly would be enforced (the state data would be frozen and impossible to change) You are safe mutating the state because you set the state at the end of the function withthis.setState
The state can not be changed while your function is executing so there is no danger of losing state data. All the examples in my answer are safe to use as they are. As for the two extra class functions, that is up to you, but should does not mean must do, JavaScript is fully capable of enforcing such rules, so if you can do, then should do is only advisory.
$endgroup$
– Blindman67
Apr 19 at 13:06
$begingroup$
Thank you very much for your explanations. Im new to React so I will study carefully the behaviour of the state
$endgroup$
– Rashomon
Apr 19 at 13:10
add a comment |
$begingroup$
Not a copy
// make a copy
let {groups} = this.state
You comment that the line following makes a copy. This is not the case, you are just creating a new reference named groups
to the array..
Use const
As you do not change the reference you should define groups
as a constant.
const {groups} = this.state;
// or
const groups = this.state.groups;
Array.push
rather than Array.concat
Array.concat
creates a new array, it is more efficient to just Array.push
a new item to the existing array
groups[gIndex].items = items.concat({id: itemId});
// can be
groups[gIndex].items.push({id: itemId});
Array.find
rather than Array.findIndex
You find the index of the group you want to modify. This complicates the code. If you use Array.find
it will return the group
and you don't need to index into groups
each time.
const gIndex= groups.findIndex(g=> g.id == groupId);
// becomes
const group = groups.find(group => group.id == groupId);
Common tasks in functions
Between the two functions you repeat some code. I would imaging that other functions also need to access groups
by id, make changes, and set the state of the new content. It would be best to provide functions to do that.
getGroupById(id) { return this.state.groups.find(group => group.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
Good naming
The functions addItem
and removeItem
do not indicate that they are related to adding/removing from a group. Better names could be removeItemFromGroup
, addItemToGroup
Be consistent
Good code style is consistent. Whether or not you use semicolons you should avoid doing it sometimes, do it always or never. (best option is use them)
Rewrite
Using the above points you could rewrite the code as the following.
Added two functions to do the common task of getting by group id and updating the state with groups.
I assume that the groupId
will exist as the code you have given indicates this to be so, if the groupId
does not exist your code would throw, and so would the following code.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
},
addItemToGroup(groupId, id) {
this.getGroupById(groupId).items.push({id});
this.updateGroups();
},
If items
are unique per group you can avoid the using Array.filter
and having to create a new array by splicing the item out of the array using Array.splice
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
const itemIdx = items.findIndex(item => item.id === id);
itemIdx > -1 && items.splice(itemIdx, 1);
this.updateGroups();
},
Or if items are not unique, or unique only sometimes you could iterate over the array removing them as you encounter them.
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
var i = items.length;
while (i--) { items[i].id === id && items.splice(i, 1) }
this.updateGroups();
},
Safer
The next example guards the function from trying to modify items if the group does not exist.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
}
},
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items.push({id});
this.updateGroups();
}
},
And just in case you don't want to duplicate items in a group.
// Only adds items if if it does not already exist in the group.
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group && !group.items.some(item => item.id === id)) {
group.items.push({id});
this.updateGroups();
}
},
$endgroup$
$begingroup$
Fantastic answer, thanks! However, I have just realizedthis.state.groups
should be treated as immutable (reactjs.org/docs/react-component.html#state). That means it should be cloned and then updated usingsetState()
strictly. This is a problem my original code has already... So maybe the final answer could be jsfiddle.net/tnug4v9L/1 ? (I had to remove some of your class methods, because the operations should be done in the clone instead of the class properties)
$endgroup$
– Rashomon
Apr 19 at 8:48
1
$begingroup$
@Rashomon Strictly would be enforced (the state data would be frozen and impossible to change) You are safe mutating the state because you set the state at the end of the function withthis.setState
The state can not be changed while your function is executing so there is no danger of losing state data. All the examples in my answer are safe to use as they are. As for the two extra class functions, that is up to you, but should does not mean must do, JavaScript is fully capable of enforcing such rules, so if you can do, then should do is only advisory.
$endgroup$
– Blindman67
Apr 19 at 13:06
$begingroup$
Thank you very much for your explanations. Im new to React so I will study carefully the behaviour of the state
$endgroup$
– Rashomon
Apr 19 at 13:10
add a comment |
$begingroup$
Not a copy
// make a copy
let {groups} = this.state
You comment that the line following makes a copy. This is not the case, you are just creating a new reference named groups
to the array..
Use const
As you do not change the reference you should define groups
as a constant.
const {groups} = this.state;
// or
const groups = this.state.groups;
Array.push
rather than Array.concat
Array.concat
creates a new array, it is more efficient to just Array.push
a new item to the existing array
groups[gIndex].items = items.concat({id: itemId});
// can be
groups[gIndex].items.push({id: itemId});
Array.find
rather than Array.findIndex
You find the index of the group you want to modify. This complicates the code. If you use Array.find
it will return the group
and you don't need to index into groups
each time.
const gIndex= groups.findIndex(g=> g.id == groupId);
// becomes
const group = groups.find(group => group.id == groupId);
Common tasks in functions
Between the two functions you repeat some code. I would imaging that other functions also need to access groups
by id, make changes, and set the state of the new content. It would be best to provide functions to do that.
getGroupById(id) { return this.state.groups.find(group => group.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
Good naming
The functions addItem
and removeItem
do not indicate that they are related to adding/removing from a group. Better names could be removeItemFromGroup
, addItemToGroup
Be consistent
Good code style is consistent. Whether or not you use semicolons you should avoid doing it sometimes, do it always or never. (best option is use them)
Rewrite
Using the above points you could rewrite the code as the following.
Added two functions to do the common task of getting by group id and updating the state with groups.
I assume that the groupId
will exist as the code you have given indicates this to be so, if the groupId
does not exist your code would throw, and so would the following code.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
},
addItemToGroup(groupId, id) {
this.getGroupById(groupId).items.push({id});
this.updateGroups();
},
If items
are unique per group you can avoid the using Array.filter
and having to create a new array by splicing the item out of the array using Array.splice
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
const itemIdx = items.findIndex(item => item.id === id);
itemIdx > -1 && items.splice(itemIdx, 1);
this.updateGroups();
},
Or if items are not unique, or unique only sometimes you could iterate over the array removing them as you encounter them.
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
var i = items.length;
while (i--) { items[i].id === id && items.splice(i, 1) }
this.updateGroups();
},
Safer
The next example guards the function from trying to modify items if the group does not exist.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
}
},
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items.push({id});
this.updateGroups();
}
},
And just in case you don't want to duplicate items in a group.
// Only adds items if if it does not already exist in the group.
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group && !group.items.some(item => item.id === id)) {
group.items.push({id});
this.updateGroups();
}
},
$endgroup$
Not a copy
// make a copy
let {groups} = this.state
You comment that the line following makes a copy. This is not the case, you are just creating a new reference named groups
to the array..
Use const
As you do not change the reference you should define groups
as a constant.
const {groups} = this.state;
// or
const groups = this.state.groups;
Array.push
rather than Array.concat
Array.concat
creates a new array, it is more efficient to just Array.push
a new item to the existing array
groups[gIndex].items = items.concat({id: itemId});
// can be
groups[gIndex].items.push({id: itemId});
Array.find
rather than Array.findIndex
You find the index of the group you want to modify. This complicates the code. If you use Array.find
it will return the group
and you don't need to index into groups
each time.
const gIndex= groups.findIndex(g=> g.id == groupId);
// becomes
const group = groups.find(group => group.id == groupId);
Common tasks in functions
Between the two functions you repeat some code. I would imaging that other functions also need to access groups
by id, make changes, and set the state of the new content. It would be best to provide functions to do that.
getGroupById(id) { return this.state.groups.find(group => group.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
Good naming
The functions addItem
and removeItem
do not indicate that they are related to adding/removing from a group. Better names could be removeItemFromGroup
, addItemToGroup
Be consistent
Good code style is consistent. Whether or not you use semicolons you should avoid doing it sometimes, do it always or never. (best option is use them)
Rewrite
Using the above points you could rewrite the code as the following.
Added two functions to do the common task of getting by group id and updating the state with groups.
I assume that the groupId
will exist as the code you have given indicates this to be so, if the groupId
does not exist your code would throw, and so would the following code.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
},
addItemToGroup(groupId, id) {
this.getGroupById(groupId).items.push({id});
this.updateGroups();
},
If items
are unique per group you can avoid the using Array.filter
and having to create a new array by splicing the item out of the array using Array.splice
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
const itemIdx = items.findIndex(item => item.id === id);
itemIdx > -1 && items.splice(itemIdx, 1);
this.updateGroups();
},
Or if items are not unique, or unique only sometimes you could iterate over the array removing them as you encounter them.
removeItemFromGroup(groupId, id) {
const items = this.getGroupById(groupId).items;
var i = items.length;
while (i--) { items[i].id === id && items.splice(i, 1) }
this.updateGroups();
},
Safer
The next example guards the function from trying to modify items if the group does not exist.
getGroupById(id) { return this.state.groups.find(g => g.id === id) },
updateGroups() { this.setState({groups: this.state.groups}) },
removeItemFromGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items = group.items.filter(item => item.id !== id);
this.updateGroups();
}
},
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group) {
group.items.push({id});
this.updateGroups();
}
},
And just in case you don't want to duplicate items in a group.
// Only adds items if if it does not already exist in the group.
addItemToGroup(groupId, id) {
const group = this.getGroupById(groupId);
if (group && !group.items.some(item => item.id === id)) {
group.items.push({id});
this.updateGroups();
}
},
answered Apr 18 at 21:27
Blindman67Blindman67
10.1k1622
10.1k1622
$begingroup$
Fantastic answer, thanks! However, I have just realizedthis.state.groups
should be treated as immutable (reactjs.org/docs/react-component.html#state). That means it should be cloned and then updated usingsetState()
strictly. This is a problem my original code has already... So maybe the final answer could be jsfiddle.net/tnug4v9L/1 ? (I had to remove some of your class methods, because the operations should be done in the clone instead of the class properties)
$endgroup$
– Rashomon
Apr 19 at 8:48
1
$begingroup$
@Rashomon Strictly would be enforced (the state data would be frozen and impossible to change) You are safe mutating the state because you set the state at the end of the function withthis.setState
The state can not be changed while your function is executing so there is no danger of losing state data. All the examples in my answer are safe to use as they are. As for the two extra class functions, that is up to you, but should does not mean must do, JavaScript is fully capable of enforcing such rules, so if you can do, then should do is only advisory.
$endgroup$
– Blindman67
Apr 19 at 13:06
$begingroup$
Thank you very much for your explanations. Im new to React so I will study carefully the behaviour of the state
$endgroup$
– Rashomon
Apr 19 at 13:10
add a comment |
$begingroup$
Fantastic answer, thanks! However, I have just realizedthis.state.groups
should be treated as immutable (reactjs.org/docs/react-component.html#state). That means it should be cloned and then updated usingsetState()
strictly. This is a problem my original code has already... So maybe the final answer could be jsfiddle.net/tnug4v9L/1 ? (I had to remove some of your class methods, because the operations should be done in the clone instead of the class properties)
$endgroup$
– Rashomon
Apr 19 at 8:48
1
$begingroup$
@Rashomon Strictly would be enforced (the state data would be frozen and impossible to change) You are safe mutating the state because you set the state at the end of the function withthis.setState
The state can not be changed while your function is executing so there is no danger of losing state data. All the examples in my answer are safe to use as they are. As for the two extra class functions, that is up to you, but should does not mean must do, JavaScript is fully capable of enforcing such rules, so if you can do, then should do is only advisory.
$endgroup$
– Blindman67
Apr 19 at 13:06
$begingroup$
Thank you very much for your explanations. Im new to React so I will study carefully the behaviour of the state
$endgroup$
– Rashomon
Apr 19 at 13:10
$begingroup$
Fantastic answer, thanks! However, I have just realized
this.state.groups
should be treated as immutable (reactjs.org/docs/react-component.html#state). That means it should be cloned and then updated using setState()
strictly. This is a problem my original code has already... So maybe the final answer could be jsfiddle.net/tnug4v9L/1 ? (I had to remove some of your class methods, because the operations should be done in the clone instead of the class properties)$endgroup$
– Rashomon
Apr 19 at 8:48
$begingroup$
Fantastic answer, thanks! However, I have just realized
this.state.groups
should be treated as immutable (reactjs.org/docs/react-component.html#state). That means it should be cloned and then updated using setState()
strictly. This is a problem my original code has already... So maybe the final answer could be jsfiddle.net/tnug4v9L/1 ? (I had to remove some of your class methods, because the operations should be done in the clone instead of the class properties)$endgroup$
– Rashomon
Apr 19 at 8:48
1
1
$begingroup$
@Rashomon Strictly would be enforced (the state data would be frozen and impossible to change) You are safe mutating the state because you set the state at the end of the function with
this.setState
The state can not be changed while your function is executing so there is no danger of losing state data. All the examples in my answer are safe to use as they are. As for the two extra class functions, that is up to you, but should does not mean must do, JavaScript is fully capable of enforcing such rules, so if you can do, then should do is only advisory.$endgroup$
– Blindman67
Apr 19 at 13:06
$begingroup$
@Rashomon Strictly would be enforced (the state data would be frozen and impossible to change) You are safe mutating the state because you set the state at the end of the function with
this.setState
The state can not be changed while your function is executing so there is no danger of losing state data. All the examples in my answer are safe to use as they are. As for the two extra class functions, that is up to you, but should does not mean must do, JavaScript is fully capable of enforcing such rules, so if you can do, then should do is only advisory.$endgroup$
– Blindman67
Apr 19 at 13:06
$begingroup$
Thank you very much for your explanations. Im new to React so I will study carefully the behaviour of the state
$endgroup$
– Rashomon
Apr 19 at 13:10
$begingroup$
Thank you very much for your explanations. Im new to React so I will study carefully the behaviour of the state
$endgroup$
– Rashomon
Apr 19 at 13:10
add a comment |
$begingroup$
For the remove function, how about using a map and filter instead:
removeItem (groupId, itemId){
let {groups} = this.state;
groups = groups.map((group) => {
if(group.id === groupId){
group.item = group.item.filter(item => item.id !== itemId);
}
return group;
});
this.setState({groups});
}
$endgroup$
add a comment |
$begingroup$
For the remove function, how about using a map and filter instead:
removeItem (groupId, itemId){
let {groups} = this.state;
groups = groups.map((group) => {
if(group.id === groupId){
group.item = group.item.filter(item => item.id !== itemId);
}
return group;
});
this.setState({groups});
}
$endgroup$
add a comment |
$begingroup$
For the remove function, how about using a map and filter instead:
removeItem (groupId, itemId){
let {groups} = this.state;
groups = groups.map((group) => {
if(group.id === groupId){
group.item = group.item.filter(item => item.id !== itemId);
}
return group;
});
this.setState({groups});
}
$endgroup$
For the remove function, how about using a map and filter instead:
removeItem (groupId, itemId){
let {groups} = this.state;
groups = groups.map((group) => {
if(group.id === groupId){
group.item = group.item.filter(item => item.id !== itemId);
}
return group;
});
this.setState({groups});
}
answered Apr 18 at 19:28
Dblaze47Dblaze47
1564
1564
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217682%2fremove-and-add-items-from-array-in-nested-javascript-object%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
$begingroup$
"Note this replicate of my code use 'group' and 'item' that its easier to read than my real code" Yes, in the lines of a Minimum Complete Verifiable Example. Which would've been commendable, if this was Stack Overflow. However, this is Code Review. Here, we require the real deal. The actual code, within it's actual context. Please include your actual code instead of a simplification. We don't deal well with those. Please consider reading our FAQ on how to get the best value out of Code Review when asking questions.
$endgroup$
– Mast
Apr 18 at 17:29