0

I have following nested objects (only 2 level example I have added to keep it simple, but it can be of any level deep).

[{
 id: 1,
 text: 'one',
 children: [{
    id: 1.1, 
    text: 'one point one', 
    children: []  
 }]
}]

let's say I have a id to find and text to replace if id matches.

How do I achieve this using lodash?

2
  • 1
    I would use JsonPath to access the nodes I need, then a plain .filter + .map to update them in-place. Commented Sep 4, 2019 at 17:14
  • ok will check this. had never heard of this. Commented Sep 4, 2019 at 17:42

3 Answers 3

6

I'm personally a big fan of lodash, but there's really no need for it here:

data = [{
    id: 1,
    text: 'one',
    children: [{
        id: 1.1,
        text: 'one point one',
        children: [
            {id: 123, text: 'blah'}
        ]
    }]
}, {
    id: 66,
    children: [
        {id: 123, text: 'blah'}
    ]
}];


let update = (id, text) => obj => {
    if (obj.id === id)
        obj.text = text;
    else if (obj.children)
        obj.children.forEach(update(id, text));
};

data.forEach(update(123, 'hello'));

console.log(data);

As pointed out in another answer, this iterates the whole tree and replaces all found ids. If you'd like to exit early and replace only the first id, the function can be adapted like this:

let update = (id, text) => obj => {
    if (obj.id === id) {
        obj.text = text;
        return true;
    }
    else if (obj.children)
        return obj.children.some(update(id, text));
};
Sign up to request clarification or add additional context in comments.

3 Comments

awesome. i also didnt want to use lodash. but i didnt know this great way. writing update in a foreach is a sort of new thing for me to know. nice stuff.
wow. double arrow functions they are called (or multi arrow). learnt great thing today. totally new to me.
@AnonymousCreator: this is a "higher order function", very handy to avoid writing .map(x => func(x, ....)) each time.
1

TL;DR Lodash has a helper for drilling down into nested objects and arrays, but only using keys and indexes, not properties of items in arrays. You'd need to make your own function for this.

Assuming that your id pattern holds and each id contains the ancestor ids (where '1.3.1' is a child of '1.3', which is a child of '1', etc.), then you'll want to split that id by your chosen delineator (. in this case) to create a path to search along. I'm also assuming that you would be using strings for the id properties, since a number type isn't going to permit multiple . characters.

const idToFind = '1.1';
const path = idToFind.split('.');
>> ['1', '1']

After that, you'll need to incrementally use more items from the list to create matching ids. If you keep track of the depth, you can slice the array to include the desired ids. Additionally, you'll want to abort the iteration if a matching id isn't found. This can be accomplished using a while loop to iterate over your path array while you haven't reached the max "depth" (the length of the path).

let children = data;
let depth = 1;
let obj = null;  // keeps track of matching objects
while (depth <= path.length) {
    const id = path.slice(0, depth).join('.');
    obj = _.find(children, (elem) => elem.id === id);
    if (!obj) return false;
    children = obj.children;
    depth += 1;
}

By the end of this iteration, you will have either returned false because we hit a non-matching id at some point, or we'll have our desired element as obj. From there, you can set the text value.

function updateText(data, idToFind, value) {
    const path = idToFind.split('.');
    let children = data;
    let depth = 1;
    let obj = null;  // keeps track of matching objects

    while (depth <= path.length) {
        const id = path.slice(0, depth).join('.');
        obj = _.find(children, (elem) => elem.id === id);
        if (!obj) return false;
        children = obj.children;
        depth += 1;
    }

    obj.text = value;
    return true;
}

This approach has the advantage of aborting the loop as soon as any unmatched id is found at each level. It also makes more use of the id pattern you have by looking for a matching id at each leve. The solution posted by georg will work, but it's going to loop through every element and nested child element until it finds the one you're looking for, so it's going to take longer with more complex data structures.

1 Comment

What is the lodash helper called?
-4

You can try with native API javascript

/*yourList.filter(elem => elem.id == yourId)
            .map(elem.text = "your new text" )*/

//---New Soluition for many fileds 
newList = yourList.filter(function(obj) {

    return obj.id ='yourid'; 

}).map(function(doctor) {

    return { // return what new object will look like

        filed1:  filed2Updated,

         filed2:  filed2Updated,

      ....

    };

}); 

4 Comments

it is more than one level. how do I deal with that?
this solution will only replace of first level. it will not replace data of children even if id matches. I haven't voted negative. :)
is this copy pasted from some place. i m not able to understand what it is
ya, I got it. but i am still not getting what is filed1: filed2Updated, filed2: filed2Updated, .... but, no worries buddy. I got the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.