randomKek
randomKek

Reputation: 1128

Recursive array parent match from array of objects

I want to create a function that gets the "parent" in a recursive array loop, and make an output array of those "used" parents.

It's a bit hard to explain, but take a look at the example:

const regions = [{
  name: 'Europe',
  subRegions: [{
    name: 'BeNeLux',
    territories: [{
      code: 'NL',
      name: 'Netherlands'
    }, {
      code: 'DE',
      name: 'Germany'
    }, {
      code: 'LU',
      name: 'Luxembourg'
    }]
  }],
  territories: [{
    code: 'UK',
    name: 'United Kingdom'
  }, {
    code: 'AL',
    name: 'Albania'
  }, {
    code: 'ZW',
    name: 'Switzerland'
  }]
}, {
  name: 'Africa',
  territories: [{
    code: 'GH',
    name: 'Ghana'
  }]
}]

const selectedTerritories = ['NL', 'UK', 'GH']

At this point I need a function, that searches for all the TOP regions of a territory by code, so the output would look like this:

const activeRegions = ['Europe', 'Africa']

A thing to note is that, there is a subRegion within Europe (BeNeLux), and the recursion gets to that point, it should NOT return BeNeLux as an active region but Europe instead.

This is what I have tried, but has duplicate subregion names and it ignores the "parent" recursion discover requirement:

const getRegionsLabelFromTerritoryList = (activeTerritories, regions, activeRegions = []) => {
  regions.forEach((region) => {
    if (region.territories) {
      region.territories.forEach(t => {
        if (activeTerritories.includes(t.code)) {
          activeRegions.push(region)
        }
      })
    }

    if (region.subRegions) {
      getRegionsLabelFromTerritoryList(region.subRegions, activeRegions)
    }
  })

  return activeRegions
}

Upvotes: 1

Views: 106

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370599

Assuming that the Africa object is supposed to be on the top level, filter the top-level objects by whether their subRegions satisfy the recursive test (a test that checks whether selectedTerritories includes the code of the object being iterated over, or whether any of the subRegions children pass the test):

const regions = [{
  name: 'Europe',
  subRegions: [{
    name: 'BeNeLux',
    territories: [{
      code: 'NL',
      name: 'Netherlands'
    }, {
      code: 'DE',
      name: 'Germany'
    }, {
      code: 'LU',
      name: 'Luxembourg'
    }]
  }],
  territories: [{
    code: 'UK',
    name: 'United Kingdom'
  }, {
    code: 'AL',
    name: 'Albania'
  }, {
    code: 'ZW',
    name: 'Switzerland'
  }]
}, {
  name: 'Africa',
  territories: [{
    code: 'GH',
    name: 'Ghana'
  }]
}];

const selectedTerritories = ['NL', 'UK', 'GH'];

const regionPasses = ({ subRegions, territories }) => (
  territories.some(({ code }) => selectedTerritories.includes(code))
  || (subRegions && subRegions.some(regionPasses))
);
    
const topSelected = regions
  .filter(regionPasses)
  .map(({ name }) => name);
console.log(topSelected);

To make things less computationally complex, you could turn selectedTerritories into a Set first (turning an O(n) operation into an O(1) operation):

const regions = [{
  name: 'Europe',
  subRegions: [{
    name: 'BeNeLux',
    territories: [{
      code: 'NL',
      name: 'Netherlands'
    }, {
      code: 'DE',
      name: 'Germany'
    }, {
      code: 'LU',
      name: 'Luxembourg'
    }]
  }],
  territories: [{
    code: 'UK',
    name: 'United Kingdom'
  }, {
    code: 'AL',
    name: 'Albania'
  }, {
    code: 'ZW',
    name: 'Switzerland'
  }]
}, {
  name: 'Africa',
  territories: [{
    code: 'GH',
    name: 'Ghana'
  }]
}];

const selectedTerritories = new Set(['NL', 'UK', 'GH']);

const regionPasses = ({ subRegions, territories }) => (
  territories.some(({ code }) => selectedTerritories.has(code))
  || (subRegions && subRegions.some(regionPasses))
);
    
const topSelected = regions
  .filter(regionPasses)
  .map(({ name }) => name);
console.log(topSelected);

You could achieve the same result with a single outer loop rather than two (.reduce or something instead of .filter followed by .map), but I think this is clearer.

Upvotes: 3

Related Questions