7
\$\begingroup\$

I'm implementing a search for a deep array. It applies the search/filter to the deepest level of children below, i.e. { name: 'foo' }, { name: 'bar' }.

If all children are filtered from the parent object, the parent is removed from the list too.

Since I want to leave the original object intact, I copy each container object and add a new children property to it.

I think this has a lot of room for improvement. It feels like it can be simplified. Also, this doesn't feel DRY.

var data = [
    { 
    name: 'a1', children: [
        { 
        name: 'b1', children: [
            { name: 'foo' }, { name: 'bar' }
        ] 
      }
    ] 
  },
  { 
    name: 'a2', children: [
        { 
        name: 'b2', children: [
            { name: 'baz' }, { name: 'bah' }
        ] 
      }
    ] 
  }
];

function filterData(data, filter){
    var result = [];
    _.forEach(data, function(firstLevelItem){
    var newFirstLevelItem = _.extend({}, firstLevelItem, { children: [] });

    _.forEach(firstLevelItem.children, function(secondLevelItem){
        var newSecondLevelItem = _.extend({}, secondLevelItem, {
        children: _.filter(secondLevelItem.children, filter)
      });

      if(newSecondLevelItem.children.length > 0) {
        newFirstLevelItem.children.push(newSecondLevelItem);
      }
    });
    if(newFirstLevelItem.children.length > 0) {
        result.push(newFirstLevelItem);
    }
  });
  return result;
}

var result = filterData(data, function(item){
    return item.name === 'foo';
});

console.log(result);

https://jsfiddle.net/b2xsna5k/

\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

I went functional on this one (as usual).

First of all, i removed the dependency from the third party library. I haven't anything in particular against that, but if the problem can be resolved easily with the standard tools, why bloat your app?

Then, since the main function deals mostly with lists, I looked for a way that would let me transform the list, filtering and transforming freely: reduce is a natural fit.

Now i can check the immediate list against a predicate (the filtering method you pass in) and recursively do the same on the list of childrens. The result of this filtering determines the final output structure.

Here's an implementation:

var data = [
    {
    name: 'a1', children: [
        {
        name: 'b1', children: [
            { name: 'gap' }, { name: 'bar' }
        ]
      }
    ]
  },
  {
    name: 'a2', children: [
        {
        name: 'b2', children: [
            { name: 'foo' }, { name: 'gap' }
        ]
      }
    ]
  }
];

function filterData(data, predicate) {

	// if no data is sent in, return null, otherwise transform the data
	return !!!data ? null : data.reduce((list, entry) => {

		let clone = null;
		if (predicate(entry)) {
			// if the object matches the filter, clone it as it is
			clone = Object.assign({}, entry)
		} else if (entry.children != null) {
			// if the object has childrens, filter the list of children
			let children = filterData(entry.children, predicate)
			if (children.length > 0) {
				// if any of the children matches, clone the parent object, overwrite
				// the children list with the filtered list
				clone = Object.assign({}, entry, {children: children})
			}
		}

		// if there's a cloned object, push it to the output list
		clone && list.push(clone)
		return list
	}, [])

}

var result = filterData(data, function(item) {
    return item.name === 'foo';
});

console.log(JSON.stringify(result, null, 2));

edit: I added some comments to the code. Shouldn't be needed, but it clarifies the steps.

\$\endgroup\$
1
  • \$\begingroup\$ Hi man. Just made some stuff base on your code but I've made one fix: not } else if (entry.children != null) { but if (entry.children != null) {. I've made separate condition and it works now \$\endgroup\$ Commented Oct 30, 2018 at 12:43

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.