Shane Padgett
Shane Padgett

Reputation: 35

Extracting array from nested object properties by nest level

I have the following JSON of structured, nested data.

[
  {
    "id": "2e270ad7-90aa-41da-bb57-a777448f5906",
    "name": "First Level 1",
    "childValues": [
      {
        "id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
        "name": "Second Level 1 "
      },
      {
        "id": "09893799-e21c-498f-96b4-e63e366a3c18",
        "name": "Second Level 2"
      }
    ]
  },
  {
    "id": "18889675-9d71-420e-84a6-3603af044b6c",
    "name": "First Level 2",
    "childValues": [
      {
        "id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
        "name": "Second Level 3"
      },
      {
        "id": "a3575212-1746-4dd3-ab52-4e37786c035c",
        "name": "Second Level 4"
      }
    ]
  },
  {
    "id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
    "name": "First Level 3",
    "childValues": [
      {
        "id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
        "name": "Second Level 5",
        "childValues": [
          {
            "id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
            "name": "Third Level 1"
          },
          {
            "id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
            "name": "Third Level 2"
          }
        ]
      },
      {
        "id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
        "name": "Second Level 6"
      }
    ]
  }
]

I am trying to extract an array of objects from this nested array structure based on a level of nesting. For instance, level 0 just gives me back all of the base object in the array, but if I ask for level 1, I am trying to get back an output of just the second level nested objects, under the childValues property, in a single array, like the following:

[
  {
    "id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
    "name": "Second Level 1 "
  },
  {
    "id": "09893799-e21c-498f-96b4-e63e366a3c18",
    "name": "Second Level 2"
  },
  {
    "id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
    "name": "Second Level 3"
  },
  {
    "id": "a3575212-1746-4dd3-ab52-4e37786c035c",
    "name": "Second Level 4"
  },
  {
    "id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
    "name": "Second Level 5",
    "childValues": [
      {
        "id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
        "name": "Third Level 1"
      },
      {
        "id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
        "name": "Third Level 2"
      }
    ]
  },
  {
    "id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
    "name": "Second Level 6"
  }
]

And if I ask for level 2, I should only get the third level objects:

[
  {
    "id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
    "name": "Third Level 1"
  },
  {
    "id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
    "name": "Third Level 2"
  }
]

The only thing I have figured out is how to completely flatten the structure recursively, but cant put my finger on how to extract a specific level.

private flat(array: any[]) {
  let result: any[] = [];
  array.forEach((a) => {
    result.push(a);
    if (Array.isArray(a.childValues)) {
      result = result.concat(this.flat(a.childValues));
    }
  });
  return result;
}

Upvotes: 0

Views: 134

Answers (4)

adiga
adiga

Reputation: 35222

You can use flatMap like this:

const input=[{id:"2e270ad7-90aa-41da-bb57-a777448f5906",name:"First Level 1",childValues:[{id:"4cecbd28-fd06-4c2a-9b57-33d4a298675c",name:"Second Level 1 "},{id:"09893799-e21c-498f-96b4-e63e366a3c18",name:"Second Level 2"}]},{id:"18889675-9d71-420e-84a6-3603af044b6c",name:"First Level 2",childValues:[{id:"b7093ca1-5fed-4eb2-b934-637bfdc6c7da",name:"Second Level 3"},{id:"a3575212-1746-4dd3-ab52-4e37786c035c",name:"Second Level 4"}]},{id:"71113ffb-62f0-4d76-941f-974be3cd35cb",name:"First Level 3",childValues:[{id:"160570a5-29aa-4fdb-bb16-d9d7637d0177",name:"Second Level 5",childValues:[{id:"2df28cb9-9ac4-478c-a2a4-6dc5206c983b",name:"Third Level 1"},{id:"a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",name:"Third Level 2"}]},{id:"6e5947ea-2c47-4d2b-8ecd-6369c728e7db",name:"Second Level 6"}]}];

const getLevel = (arr = [], required, current = 0) =>
    required === current 
      ? arr
      : arr.flatMap(a => getLevel(a.childValues, required, current + 1))

console.log("Second Level: \n ", getLevel(input, 1))
console.log("Third Level: \n ", getLevel(input, 2))

If flatMap is not supported, you can use

[].concat(...arr.map(a => getLevel(a.childValues, required, current + 1)))

Upvotes: 1

uautkarsh
uautkarsh

Reputation: 502

Following should work:

var data = [
  {
    "id": "2e270ad7-90aa-41da-bb57-a777448f5906",
    "name": "First Level 1",
    "childValues": [
      {
        "id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
        "name": "Second Level 1"
      },
      {
        "id": "09893799-e21c-498f-96b4-e63e366a3c18",
        "name": "Second Level 2"
      }
    ]
  },
  {
    "id": "18889675-9d71-420e-84a6-3603af044b6c",
    "name": "First Level 2",
    "childValues": [
      {
        "id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
        "name": "Second Level 3"
      },
      {
        "id": "a3575212-1746-4dd3-ab52-4e37786c035c",
        "name": "Second Level 4"
      }
    ]
  },
  {
    "id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
    "name": "First Level 3",
    "childValues": [
      {
        "id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
        "name": "Second Level 5",
        "childValues": [
          {
            "id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
            "name": "Third Level 1"
          },
          {
            "id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
            "name": "Third Level 2"
          }
        ]
      },
      {
        "id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
        "name": "Second Level 6"
      }
    ]
  }
];

function getData(data, targetLevel, currentLevel = 0) {
    // If this is the target level, then extract the
    // data we need from each item, and return the array
    if (currentLevel == targetLevel) {
        return data;
    }
    // Otherwise, run a map over the items, and if they have
    // 'childValues', then recurs, but increment the value of
    // 'current level' it will be iterating on
    // Because 'map' will return array of array, merge them
    // to a single array
    return [].concat(...data.map(item => {
        if (item.childValues) {
            return getData(item.childValues, targetLevel, currentLevel + 1);
        };
        return [];
    }));
}

document.getElementById("dataLevel0").innerHTML = JSON.stringify(getData(data, 0), null, 4);
document.getElementById("dataLevel1").innerHTML = JSON.stringify(getData(data, 1), null, 4);
document.getElementById("dataLevel2").innerHTML = JSON.stringify(getData(data, 2), null, 4);
<div>
Level 1:
</div>
<pre id="dataLevel0">
</pre>
<div>
Level 2:
</div>
<pre id="dataLevel1">
</pre>
<div>
Level 3:
</div>
<pre id="dataLevel2">
</pre>

Upvotes: 0

Shane Padgett
Shane Padgett

Reputation: 35

I was able to achieve this with the following function.

function getArrayByNthLevelOfPropName(array, propName, levelToGet, currentLevel = 0) {
  let result = [];
  array.forEach((a) => {
    if (levelToGet === currentLevel) {
      result.push(a);
    }

    if (Array.isArray(a[propName]) && levelToGet !== currentLevel) {
      result = result.concat(getArrayByNthLevelOfPropName(a[propName], propName, levelToGet, ++currentLevel));
      currentLevel -= 1;
    }
  });

  return result;
}

Upvotes: 1

Dov Rine
Dov Rine

Reputation: 840

Here's a cleaner version of Shane Padgett's function:

const getArrayByNthLevel = (array, levelToGet, currentLevel=0) => array.reduce((retval, a) => {
        levelToGet === currentLevel
            ? retval.push(a)
            : Array.isArray(a.childValues)
                ? retval = retval.concat(getArrayByNthLevel(a.childValues, levelToGet, currentLevel + 1))
                : false
        return retval;
    }, []);

Upvotes: 1

Related Questions