1

For an object of this structure:

const myObj = {
  id: 1,
  name: '1',
  children: [
    {
      id: 2,
      name: '2',
      children: [
        {
          id: 3,
          name: '3',
          children: []
        }
      ]
    },
    {
      id: 4,
      name: '4',
      children: [
        {
          id: 5,
          name: '5',
          children: [
            {
              id: 6,
              name: '6',
              children: [
                {
                  id: 7,
                  name: '7',
                  children: []
                }
              ]
            }
          ]
        }
      ]
    },
  ]
}

How would I get an object by value (id)? So I'm looking for a function where if I call this:

findObj(myObj, 6);

It would return this:

       {
          id: 6,
          name: '6',
          children: [
            {
              id: 7,
              name: '7',
              children: []
            }
          ]
        }

Another example:

findObj(myObj, 3);

Would return this:

    {
      id: 3,
      name: '3',
      children: []
    }

I know I need a recursion function. Heres what I have so far:

  findObj(obj, id) {
    if (obj.id === id) {
      return obj;
    }

    obj.forEach(x => {
      if (x.id === id) {
        return x;
      } else {
        this.findObj(x.children, id);
      }
    });
  }

(This is in an angular class)

halfer
  • 19,824
  • 17
  • 99
  • 186
cup_of
  • 6,397
  • 9
  • 47
  • 94
  • search for parent is missleading, because you seach for the item itself. btw, what have you tried? – Nina Scholz Mar 03 '19 at 22:06
  • yeah ive edited my answer just now haha @NinaScholz – cup_of Mar 03 '19 at 22:07
  • Consider what it means in the parent function if you `return` from a callback function. Then consider what `Array#forEach` does. – tehhowch Mar 03 '19 at 22:09
  • 2
    I implement a generic `deepFind` solution in [this Q&A](https://stackoverflow.com/a/50538352/633183). I think you will find it useful :D – Mulan Mar 03 '19 at 22:46

3 Answers3

4

At first sight I see some problems.

If you already have the ob.id === id inside the function, you don't need to double check for it later. Also, if findObj is a function defined in the scope like any other variable and is not in the "class" definition, you don't need to call it through this, the same way the first call to the findObj you do it without the this keyword.

Also, at first you pass an object to your method, but then you pass an object array (the obj.children), and also you check for the id inside a forEach method, where you pass a function (the x => { ... thingy), so when you return, you return from that function instead of the main function.

I've fixed it for you. I recommend you to understand what is happening and learn from it, as this will not happen if you know what you are doing (debug is your master here).

findObj(obj, id) {
    if (obj.id === id) {
        return obj;
    }
    for (var i = 0; i < obj.children.length; i++) { // No forEach, so when we return, we actually return from findObj
        var r = findObj(obj.children[i], id); // We see if we have any return and let the "check logic" be defined on one location instead of duplicated.
        if (r) {
            return r;
        }
    }
}
Jorge Fuentes González
  • 11,568
  • 4
  • 44
  • 64
  • 1
    There are also good alternatives, as described in the [forEach MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Description): `There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool.` – Joliver Mar 03 '19 at 22:48
1

Two issues:

  • your return x; statement only returns from the forEach callback, not from the findObj function. Don't use forEach! A simple for … of loop will do
  • if your recursive call find the object and returns it, you ignore it and just continue iterating. Check whether you got a result and handle it appropriately.

I guess you're not looking for a full solution, so I leave that as an exercise :-)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

You could check the object or check the children.

function find(object, id) {
    var result;
    if (object.id === id) return object;
    object.children.some(o => result = find(o, id));
    return result;
}

const object = { id: 1, name: '1', children: [{ id: 2, name: '2', children: [{ id: 3, name: '3', children: [] }] }, { id: 4, name: '4', children: [{ id: 5, name: '5', children: [{ id: 6, name: '6', children: [{ id: 7, name: '7', children: [] }] }] }] }] };

console.log(find(object, 5));
console.log(find(object, 6));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392