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;
}







2












$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?










share|improve this question











$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


















2












$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?










share|improve this question











$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














2












2








2


1



$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?










share|improve this question











$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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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


















  • $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










2 Answers
2






active

oldest

votes


















1












$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();
}
},





share|improve this answer









$endgroup$













  • $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




    $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



















1












$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});
}





share|improve this answer









$endgroup$














    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
    });


    }
    });














    draft saved

    draft discarded


















    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









    1












    $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();
    }
    },





    share|improve this answer









    $endgroup$













    • $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




      $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
















    1












    $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();
    }
    },





    share|improve this answer









    $endgroup$













    • $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




      $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














    1












    1








    1





    $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();
    }
    },





    share|improve this answer









    $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();
    }
    },






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Apr 18 at 21:27









    Blindman67Blindman67

    10.1k1622




    10.1k1622












    • $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




      $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$
      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




      $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$
    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













    1












    $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});
    }





    share|improve this answer









    $endgroup$


















      1












      $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});
      }





      share|improve this answer









      $endgroup$
















        1












        1








        1





        $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});
        }





        share|improve this answer









        $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});
        }






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Apr 18 at 19:28









        Dblaze47Dblaze47

        1564




        1564






























            draft saved

            draft discarded




















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

            How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

            Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...