How to convert a flat data structure into a tree-like structure?

Viewed 130

Let's say I have two helper functions converting flat array of {} into tree-like structure. Consider the following flat data:

const data = [
     {
            "ID": 1,
            "Tier_1": "DataSource1",
            "Tier_2": "Area",
            "Tier_3": "General",
        },
        {
            "ID": 2,
            "Tier_1": "DataSource1",
            "Tier_2": "Financial",
            "Tier_3": "General",
        },
        {
            "ID": 3,
            "Tier_1": "DataSource1",
            "Tier_2": "Area",
            "Tier_3": "General",
        },
        {
            "ID": 4,
            "Tier_1": "DataSource2",
            "Tier_2": "Area",
            "Tier_3": "General",
        },
        {
            "ID": 5,
            "Tier_1": "DataSource2",
            "Tier_2": "Area",
            "Tier_3": "Management Plan",
        }
]

The data contains three rows of some hierarchical information, that I would like to convert into tree-like structure, like this (expected output):

(Last children are actual DB objects, but distributed on tree)

const output = {
  "DataSource1: {
    "Area": {
        {
            "ID": 1,
            "Tier_1": "DataSource1",
            "Tier_2": "Area",
            "Tier_3": "General",
        },
        {
            "ID": 3,
            "Tier_1": "DataSource1",
            "Tier_2": "Area",
            "Tier_3": "General",
        },
      },
      "Financial": [
        {
            "ID": 2,
            "Tier_1": "DataSource1",
            "Tier_2": "Financial",
            "Tier_3": "General",
        },
      ]
  },
  "DataSource2: {
      "Area": [
          {
            "ID": 4,
            "Tier_1": "DataSource2",
            "Tier_2": "Area",
            "Tier_3": "General",
          },
          {
            "ID": 5,
            "Tier_1": "DataSource2",
            "Tier_2": "Area",
            "Tier_3": "Management Plan",
          }
       ]
      }
  }
}

I actually managed to create functions to do accomplish this, but I they are not so flexible (the depth / dim is fixed, which is stated in each function name)

Function returning 2-dimensional tree:

const getDataCategoriesTwoDim = (data, mainCategory) => {
  const dataFields = [...data];
  let map = {};

  for (let i = 0; i < dataFields.length; i += 1) {
    const currentField = dataFields[i];
    const currentCategory = currentField[mainCategory];

    if (!map[currentCategory]) {
      map[currentCategory] = [];
    }
    map[currentCategory].push(currentField);
  }

  return map;
};

Function returning three-dimension tree:

const getDataCategoriesThreeDim = (data, mainCategory, subCategory) => { // DIFF
  const dataFields = [...data];
  let map = {};

  for (let i = 0; i < dataFields.length; i += 1) {
    const currentField = dataFields[i];
    const currentCategory = currentField[mainCategory];
    const currentSubcategory = currentField[subCategory]; // DIFF

    if (!map[currentCategory]) {
      map[currentCategory] = {}; /DIFF
    }
    if (!map[currentCategory][currentSubcategory]) { // DIFF
      map[currentCategory][currentSubcategory] = []; // DIFF
    } // DIFF
    map[currentCategory][currentSubcategory].push(currentField); // DIFF
  }

  return map;
};

You can call both like this and get expected result:

  getDataCategoriesTwoDim(data, 'Tier_2');
  getDataCategoriesThreeDim(data, 'Tier_2', 'Tier_3');

As you can see, there is so much code repetition and copy paste. I marked differences in comments. How could I rewrite the code into one function, so that I can set 2, 3 or more dimensions?

1 Answers

You could take the keys for nesting properties and add an array for the last key instead of an object. Later push the object to the nested array.

const
    groupBy = (data, keys) => data.reduce((r, o) => {
        keys
            .reduce((p, k, i, a) =>
                 p[o[k]] = p[o[k]] || (i + 1 === a.length ? [] : {}), r)
            .push(o);
        return r;
    }, Object.create(null)),
    data = [{ ID: 1, Tier_1: "DataSource1", Tier_2: "Area", Tier_3: "General" }, { ID: 2, Tier_1: "DataSource1", Tier_2: "Financial", Tier_3: "General" }, { ID: 3, Tier_1: "DataSource1", Tier_2: "Area", Tier_3: "General" }, { ID: 4, Tier_1: "DataSource2", Tier_2: "Area", Tier_3: "General" }, { ID: 5, Tier_1: "DataSource2", Tier_2: "Area", Tier_3: "Management Plan" }],
    result1 = groupBy(data, ["Tier_1", "Tier_2"]),
    result2 = groupBy(data, ["Tier_1", "Tier_2", "Tier_3"]);

console.log(result1);
console.log(result2);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related