helion3
helion3

Reputation: 37361

Find object by match property in nested array

I'm not seeing a way to find objects when my condition would involve a nested array.

var modules = [{
    name: 'Module1',
    submodules: [{
        name: 'Submodule1',
        id: 1
      }, {
        name: 'Submodule2',
        id: 2
      }
    ]
  }, {
    name: 'Module2',
    submodules: [{
        name: 'Submodule1',
        id: 3
      }, {
        name: 'Submodule2',
        id: 4
      }
    ]
  }
];

This won't work because submodules is an array, not an object. Is there any shorthand that would make this work? I'm trying to avoid iterating the array manually.

_.where(modules, {submodules:{id:3}});

Upvotes: 41

Views: 80463

Answers (6)

NicolayM
NicolayM

Reputation: 231

If you want to search all by keyword:

const searchWord = 'Hello world'.trim().toLowerCase();
const result = JSON.stringify(object).trim().toLowerCase().includes(searchWord);
  1. Trim and lowercase search word(s).
  2. Stringify object, trim and lowercase.
  3. Check if it includes the search word(s).

Upvotes: 0

Joel Vega
Joel Vega

Reputation: 61

Another solution can be:

const var = _.find(modules, (item) => {
        return (item.submodules.id === 2)
      })

You can either user _.find to get the first occurrence or _.filter to get all ot them.

Upvotes: 0

AWolf
AWolf

Reputation: 8970

I looked into this and I think the best option is to use Deepdash. It's a collection of methods to do deeply filter, find etc.

Sure it would be possible with lodash alone but with Deepdash it's easier.

I tried to convert the previous answer to the latest Lodash version but that was not working. Every method was deprecated in v4 of Lodash. Possible replacements: select = map or filter, any = some, where = filter)

findDeep returns an object with some information to the found item (just some values, see the docs for more details):

  • value is the object found
  • key that's the index in the nested array
  • parent the parent of the value

So the code for the findDeep looks like:

const modules = [{
  name: 'Module1',
  submodules: [{
    name: 'Submodule1',
    id: 1
  }, {
    name: 'Submodule2',
    id: 2
  }]
}, {
  name: 'Module2',
  submodules: [{
    name: 'Submodule1',
    id: 3
  }, {
    name: 'Submodule2',
    id: 4
  }]
}];

const getModule = (modules, id) =>
  _.findDeep(modules, module => module.id === id, {
    childrenPath: "submodules"
  });


const resultEl = document.getElementById("result");
const foundModule = getModule(modules, 3).value;

resultEl.innerText = JSON.stringify(foundModule, null, 2);
<script src="https://cdn.jsdelivr.net/npm/deepdash/browser/deepdash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script>
  deepdash(_);
</script>

<h2>Example to get module with id = 3</h2>
<pre id="result"></pre>

Upvotes: 1

martinoss
martinoss

Reputation: 5458

Lodash allows you to filter in nested data (including arrays) like this:

_.filter(modules, { submodules: [ { id: 2 } ]});

Upvotes: 90

Adam Boduch
Adam Boduch

Reputation: 11211

Here's what I came up with:

_.find(modules, _.flow(
    _.property('submodules'),
    _.partialRight(_.some, { id: 2 })
));
// → { name: 'Module1', ... }

Using flow(), you can construct a callback function that does what you need. When call, the data flows through each function. The first thing you want is the submodules property, and you can get that using the property() function.

The the submodules array is then fed into some(), which returns true if it contains the submodule you're after, in this case, ID 2.

Replace find() with filter() if you're looking for multiple modules, and not just the first one found.

Upvotes: 28

Mabedan
Mabedan

Reputation: 907

I think your best chance is using a function, for obtaining the module.

_.select(modules, function (module) {
  return _.any(module.submodules, function (submodule) {
    return _.where(submodule, {id:3});
  });
});

try this for getting the submodule

.where(.pluck(modules, "submodules"), {submodules:{id:3}});

Upvotes: 1

Related Questions