Maksym Dudyk
Maksym Dudyk

Reputation: 1164

How to convert array of objects' data into a more meaningful structure of nested objects and arrays?

Data is useless unless it is well structured. I want to convert array of objects into a more meaningfully structured object via vanilla JavaScript and by this - to lessen entropy in the world :)

Companies' values form nested named arrays as well as vehicles' values form nested named objects. The hardest task was to set if-statements dynamically. I got stuck in the end of the code, yet, hoping that a JS professional could help me out.

// Source data format

var inputs = [
    {"vehicle":"car", "company":"Toyota", "model":"Corolla"},
    {"vehicle":"car", "company":"Toyota", "model":"Rav4"},
    {"vehicle":"car", "company":"Toyota", "model":"Camry"},
    {"vehicle":"car", "company":"Chevrolet", "model":"Malibu"},
    {"vehicle":"car", "company":"Chevrolet", "model":"Camaro"},
    {"vehicle":"rocket", "company":"Tesla", "model":"SpaceX"}
];

// Target data format

const data = {
    car:{
        Toyota:[
            {"vehicle"="car","company"="Toyota", "model"="Corolla"},
            {"vehicle"="car","company"="Toyota", "model"="Rav4"},
            {"vehicle"="car","company"="Toyota", "model"="Camry"}
        ],
        Chevrolet:[
            {"vehicle"="car","company"="Chevrolet", "model"="Malibu"},
            {"vehicle"="car","company"="Chevrolet", "model"="Camaro"}
        ]
    },
    rocket:{
        Tesla:[
            {"vehicle"="rocket","company"="Tesla", "model"="SpaceX"}
        ]
    }
};

// Unfinished solution

// Get all vehicle names.
var vehicles = [];
for (var [key, obj] of inputs.entries()) {
    vehicles.push(obj.vehicle);
}

// Single out only unique vehicle names.
var uniqueVehicles = [...new Set(vehicles)];

// Get all company names.
var arr = [];
for (var [key, obj] of inputs.entries()) {
    arr.push(obj.company);
}

// Single out only unique company names.
var uniqueCompanies = [...new Set(arr)];

// Group objects into arrays by company names.
var dataProperties = {};
for (var comp of uniqueCompanies) {
    dataProperties[comp] = inputs.filter(obj => obj.company === comp);  
}

// Group objects into arrays by vehicle names.
var data = {};
for (var vehi of uniqueVehicles) {
    data[vehi] = inputs.filter(o => o.vehicle === vehi);
}
// data;
// dataProperties;

Upvotes: 1

Views: 89

Answers (2)

Nina Scholz
Nina Scholz

Reputation: 386654

You could take a more advanced version with an array of the wanted keys for nesting the wanted properties. This approach is a bit different than the other answer.

The key part is to generate, if necessary and return the last array for pushing an object to the result set,

groups.reduce((p, k, i, { length }) => p[o[k]] = p[o[k]] || (i + 1 === length ? [] : {}), r)

where you have

  • p an object as accumulator, starting with the final result object,
  • k the key for grouping,
  • i the actual index of the groups array,
  • a destructured length of the groups array for a following check, if the last item is used.

Inside of the callback, the value of the wanted property o[k] is used to access the object p and if not truthy, like undefined, then an array of if the last key is taken, then an array is taken.

var array = [{ vehicle: "car", company: "Toyota", model: "Corolla" }, { vehicle: "car", company: "Toyota", model: "Rav4" }, { vehicle: "car", company: "Toyota", model: "Camry" }, { vehicle: "car", company: "Chevrolet", model: "Malibu" }, { vehicle: "car", company: "Chevrolet", model: "Camaro" }, { vehicle: "rocket", company: "Tesla", model: "SpaceX" }],
    groups = ["vehicle", "company"],
    result = array.reduce((r, o) => {
        groups
            .reduce((p, k, i, { length }) => p[o[k]] = p[o[k]] || (i + 1 === length ? [] : {}), r)
            .push(o);
        return r;
    }, {});

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

Upvotes: 1

Nicholas Tower
Nicholas Tower

Reputation: 85022

Here's how i would do it:

function transform(data) {
  let result = {};
  data.forEach(element => {
    // Reuse the existing vehicle object, or create an empty one if it doesn't exist
    result[element.vehicle] = result[element.vehicle] || {};
    // Reuse the existing company array, or create an empty one if it doesn't exist
    result[element.vehicle][element.company] = result[element.vehicle][element.company] || [];
    result[element.vehicle][element.company].push(element);
  })
  return result;
}

const inputs = [
  {"vehicle":"car", "company":"Toyota", "model":"Corolla"},
  {"vehicle":"car", "company":"Toyota", "model":"Rav4"},
  {"vehicle":"car", "company":"Toyota", "model":"Camry"},
  {"vehicle":"car", "company":"Chevrolet", "model":"Malibu"},
  {"vehicle":"car", "company":"Chevrolet", "model":"Camaro"},
  {"vehicle":"rocket", "company":"Tesla", "model":"SpaceX"}
];

const output = transform(inputs);
console.log(output);

Upvotes: 2

Related Questions