19

I have this array:

[
    {
        id: 1,
        name: 'test 1',
        children: []
    },
    {
        id: 2,
        name: 'test 2',
        children: [
            {
                id: 4,
                name: 'test 4'
            }
        ]
    },
    {
        id: 3,
        name: 'test 3',
        children: []
    }
]

How can I filter by the id property in both this array and the nested children arrays?

For example, searching for id = 3, should return the test 3 object, and searching for id = 4 should return the test 4 object.

2
  • Is the nesting depth of the children arrays fixed to one level, or is it arbitrary? Commented Jun 8, 2015 at 20:17
  • @AdamBoduch it is fixed to one level Commented Jun 8, 2015 at 20:53

4 Answers 4

35

Using lodash, you can do something like this:

_(data)
    .thru(function(coll) {
        return _.union(coll, _.map(coll, 'children') || []);
    })
    .flatten()
    .find({ id: 4 });

Here, thru() is used to initialize the wrapped value. It's returning the union of the original array, and the nested children. This array structure is then flattened using flatten(), so you can find() the item.

Sign up to request clarification or add additional context in comments.

5 Comments

Excellent! For lodash v4.0 replace pluck with map.
Also, I need to mention that unfortunately this is slower for small arrays than iterating.
Bare in mind that this implementation is going only one level deep. If your tree is more than one level deep it won't work as intended.
You could use flattenDeep or flattenDepth
This code does not work if we have 1 more nested child to find
6

That's a very simple tree traversal task. The easiest way to solve it is recursion (link to jsbin). It will work with any depth (with recursion limit of course) and it's one of the fastest ways with the worst complexity O(n):

function find(id, items) {
  var i = 0, found;

  for (; i < items.length; i++) {
    if (items[i].id === id) {
      return items[i];
    } else if (_.isArray(items[i].children)) {
      found = find(id, items[i].children);
      if (found) {
        return found;
      }
    }
  }
}

Update:

To find all matches - a slightly modified function (jsbin link above is updated):

function findAll(id, items) {
  var i = 0, found, result = [];

  for (; i < items.length; i++) {
    if (items[i].id === id) {
      result.push(items[i]);
    } else if (_.isArray(items[i].children)) {
      found = findAll(id, items[i].children);
      if (found.length) {
        result = result.concat(found);
      }
    }
  }

  return result;
}

2 Comments

It will return only one item. What if I need all the matching elements?
@Ashwin, please see the update of the answer. But keep in mind that in case of findAll the whole tree would be analyzed (find would stop at the very first matching).
6

Another lodash option with children key and unlimited levels deep.

const flattenItems = (items, key) => {
    return items.reduce((flattenedItems, item) => {
        flattenedItems.push(item)
        if (Array.isArray(item[key])) {
            flattenedItems = flattenedItems.concat(flattenItems(item[key], key))
        }
        return flattenedItems
    }, [])
}

const item = find(flattenItems(items, 'children'), ['id', 4])

1 Comment

Great, this works as adviced!
0

You can achieve this by using pure javascript with ES6 syntax:

  • First you can flatten the array with .reduce function
  • Then you can use .find to search for id you want to find

const arr = [
  {
    id: 1,
    name: 'test 1',
    children: []
  },
  {
    id: 2,
    name: 'test 2',
    children: [
      {
        id: 4,
        name: 'test 4'
      }
    ]
  },
  {
    id: 3,
    name: 'test 3',
    children: []
  }
]

const flattenData = arr.reduce((newArr, arr) => {
  const {children, ...rest } = arr;
  newArr.push(rest);
  return newArr.concat(children)
}, [])


console.log(flattenData.find(d=>d.id===4))

Comments

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.