Home > front end >  Flatten nested JSON to array of unique objects without indexes
Flatten nested JSON to array of unique objects without indexes

Time:01-26

I have this simple nested object which I need to flatten to be able to insert it into my database.

const input = {
  name: "Benny",
  department: {
    section: "Technical",
    branch: {
      timezone: "UTC",
    },
  },
  company: [
    {
      name: "SAP",
      customers: ["Ford-1", "Nestle-1"],
    },
    {
      name: "SAP",
      customers: ["Ford-2", "Nestle-2"],
    },
  ],
};

The desired result is like this, each value in the arrays results in a new sub-object stored in an array:

[
  {
    name: "Benny",
    "department.section": "Technical",
    "department.branch.timezone": "UTC",
    "company.name": "SAP",
    "company.customers": "Ford-1",
  },
  {
    name: "Benny",
    "department.section": "Technical",
    "department.branch.timezone": "UTC",
    "company.name": "SAP",
    "company.customers": "Nestle-1",
  },
  {
    name: "Benny",
    "department.section": "Technical",
    "department.branch.timezone": "UTC",
    "company.name": "SAP",
    "company.customers": "Ford-2",
  },
  {
    name: "Benny",
    "department.section": "Technical",
    "department.branch.timezone": "UTC",
    "company.name": "SAP",
    "company.customers": "Nestle-2",
  },
]

Instead of the result below which all fields stored in single object with indexes:

{
  name: 'Benny',
  'department.section': 'Technical',
  'department.branch.timezone': 'UTC',
  'company.0.name': 'SAP',
  'company.0.customers.0': 'Ford-1',
  'company.0.customers.1': 'Nestle-1',
  'company.1.name': 'SAP',
  'company.1.customers.0': 'Ford-2',
  'company.1.customers.1': 'Nestle-2'
}

My code looks like this:

function flatten(obj) {
  let keys = {};
  for (let i in obj) {
    if (!obj.hasOwnProperty(i)) continue;
    if (typeof obj[i] == "object") {
      let flatObj = flatten(obj[i]);
      for (let j in flatObj) {
        if (!flatObj.hasOwnProperty(j)) continue;
        keys[i   "."   j] = flatObj[j];
      }
    } else {
      keys[i] = obj[i];
    }
  }
  return keys;
}

Thanks in advance!

CodePudding user response:

The closest (legitimate) I could get to your desired result is:

[
  {
    "name": "Benny",
    "department.section": "Technical",
    "department.branch.timezone": "UTC",
    "company.name": "SAP",
    "company.customers.0": "Ford-1",
    "company.customers.1": "Nestle-1"
  },
  {
    "name": "Benny",
    "department.section": "Technical",
    "department.branch.timezone": "UTC",
    "company.name": "SAP",
    "company.customers.0": "Ford-2",
    "company.customers.1": "Nestle-2"
  }
]

I had to create a wrapper function called flattenBy that handles mapping the data by a particular key e.g. company and passes it down to your flatten function (along with the current index).

const flatten = (obj, key, index) => {
  let keys = {};
  for (let i in obj) {
    if (!obj.hasOwnProperty(i)) continue;
    let ref = i !== key ? obj[i] : obj[i][index];
    if (typeof ref == 'object') {
      let flatObj = flatten(ref, key);
      for (let j in flatObj) {
        if (!flatObj.hasOwnProperty(j)) continue;
        keys[i   '.'   j] = flatObj[j];
      }
    } else { keys[i] = obj[i]; }
  }
  return keys;
}

const flattenBy = (obj, key) =>
  obj[key].map((item, index) => flatten(obj, key, index));


const input = {
  name: "Benny",
  department: { section: "Technical", branch: { timezone: "UTC" } },
  company: [
    { name: "SAP", customers: ["Ford-1", "Nestle-1"] },
    { name: "SAP", customers: ["Ford-2", "Nestle-2"] },
  ]
};

console.log(JSON.stringify(flattenBy(input, 'company'), null, 2));
.as-console-wrapper { top: 0; max-height: 100% !important; }

CodePudding user response:

You could keep the indices for nested arrays.

const
    getFlat = o => Object.entries(o).flatMap(([k, v]) => v && typeof v === 'object'
        ? getFlat(v).map(([l, v]) => [`${k}.${l}`, v])
        : [[k, v]]
    ),
    fn = ({ company, ...o }) => {
        const rest = Object.fromEntries(getFlat(o));
        return company.map(company => ({
            ...rest,
            ...Object.fromEntries(getFlat({ company }))
        }));
    }
    input = { name: "Benny", department: { section: "Technical", branch: { timezone: "UTC" } }, company: [{ name: "SAP", customers: ["Ford-1", "Nestle-1"] }, { name: "SAP", customers: ["Ford-2", "Nestle-2"] }] },
    result = fn(input);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

  • Related