Mad-D
Mad-D

Reputation: 4669

Build nested JSON in Javascript

I would like to replicate this example data set into nested JSON using JavaScript or Angularjs or any javascript library.

Data:

PrimaryId,FirstName,LastName,City,CarName,DogName
100,John,Smith,NewYork,Toyota,Spike
100,John,Smith,NewYork,BMW,Spike
100,John,Smith,NewYork,Toyota,Rusty
100,John,Smith,NewYork,BMW,Rusty
101,Ben,Swan,Sydney,Volkswagen,Buddy
101,Ben,Swan,Sydney,Ford,Buddy
101,Ben,Swan,Sydney,Audi,Buddy
101,Ben,Swan,Sydney,Volkswagen,Max
101,Ben,Swan,Sydney,Ford,Max
101,Ben,Swan,Sydney,Audi,Max
102,Julia,Brown,London,Mini,Lucy

Javascript:

var file = reader.result; 
var singleRow = readerFile.split(/\r\n|\n/);
var header = singleRow[0].split(',');
var result =[];  

    for ( var i=1; i < file.length; i++ ){                                      
        var elementData = singleRow[i].split(',');                              
        elementData = elementData.filter(function(n){ return n != "" });        
        var Obj = {};                                                           

        for ( var j=0; j < header.length; j++ ){
            Obj[header[j]] = elementData[j];
            /*
              - How can i build child object and append back to Obj before j loop
              - How can i build multiple child for same parent 
            */                  
        }
        result.push(Obj);                                                       
    }
    console.log(" Print the JSON Object : " + JSON.stringify(result));

Desired Output:

{
    "data": [
        {
            "City": "NewYork", 
            "FirstName": "John", 
            "PrimaryId": 100, 
            "LastName": "Smith", 
            "CarName": [
                "Toyota", 
                "BMW"
            ], 
            "DogName": [
                "Spike", 
                "Rusty"
            ]
        }, 
        {
            "City": "Sydney", 
            "FirstName": "Ben", 
            "PrimaryId": 101, 
            "LastName": "Swan", 
            "CarName": [
                "Volkswagen", 
                "Ford", 
                "Audi"
            ], 
            "DogName": [
                "Buddy", 
                "Max"
            ]
        }, 
        {
            "City": "London", 
            "FirstName": "Julia", 
            "PrimaryId": 102, 
            "LastName": "Brown", 
            "CarName": [
                "Mini"
            ], 
            "DogName": [
                "Lucy"
            ]
        }
    ]
}

If Firstname, Lastname and City has same values then CarName and DogName values should be child object under the same parent

Upvotes: 2

Views: 11131

Answers (3)

Jaggler3
Jaggler3

Reputation: 304

Made a fiddle for you, it gives the desired output with some things in a different order than you presented.

You can save the indexes of the headers:

var Index = {};
for(var k = 0; k < header.length; k++)
{
    Index[header[k]] = k;
}

And keep a list of cities:

var cities = [];
....
cities.push(data[Index["City"]]);

To use for later so that you don't keep making more objects if the city already exists:

obj = result.data[cities.indexOf(data[Index["City"]])];

The JSFiddle: https://jsfiddle.net/3u28aon3/1/

Upvotes: 3

Yangshun Tay
Yangshun Tay

Reputation: 53169

I reformatted your initial code a little bit, but it doesn't change the initial logic. One key observation is that even if the FirstName, LastName and City are the same, that may not be a unique person, hence you should use the PrimaryId instead, to determine uniqueness.

Look at the post-processing section for the new code:

const data = `PrimaryId,FirstName,LastName,City,CarName,DogName
100,John,Smith,NewYork,Toyota,Spike
100,John,Smith,NewYork,BMW,Spike
100,John,Smith,NewYork,Toyota,Rusty
100,John,Smith,NewYork,BMW,Rusty
101,Ben,Swan,Sydney,Volkswagen,Buddy
101,Ben,Swan,Sydney,Ford,Buddy
101,Ben,Swan,Sydney,Audi,Buddy
101,Ben,Swan,Sydney,Volkswagen,Max
101,Ben,Swan,Sydney,Ford,Max
101,Ben,Swan,Sydney,Audi,Max
102,Julia,Brown,London,Mini,Lucy`;

var singleRow = data.split(/\r\n|\n/);
var header = singleRow[0].split(',');
var result =[];

for (var i = 1; i < singleRow.length; i++) {
  var elementData = singleRow[i].split(',');
  elementData = elementData.filter(function(n) { return n != '' });
  var Obj = {};
  for ( var j=0; j < header.length; j++ ){
    Obj[header[j]] = elementData[j];
  }
  result.push(Obj);
}

console.log(JSON.stringify(result, null, 2));


// Post-processing code starts here
const people = {};

// Create a map of unique people first
result.forEach(function (object) {
  if (!people[object.PrimaryId]) {
    people[object.PrimaryId] = {
      City: object.City,
      FirstName: object.FirstName,
      PrimaryId: object.PrimaryId,
      LastName: object.LastName,
      CarName: [],
      DogName: [],
    };
  }
  // As you iterate through your results, if this person already exists
  // add to their array of car and dogs.
  people[object.PrimaryId].CarName.push(object.CarName);
  people[object.PrimaryId].DogName.push(object.DogName);
});

// Convert back into an array
const peopleList = [];
Object.keys(people).forEach(function (primaryId) {
  peopleList.push(people[primaryId]);
})

console.log(peopleList);

Upvotes: 5

Andrew Eisenberg
Andrew Eisenberg

Reputation: 28757

First of all, since you already know the property names, there's no point in parsing the first row.

I would do something like this:

let results = {};
for (let i = 1; i < file.length; i++) {
  let entry = getEntry(results, file[i][0]);
  entry.DogName.push(file[i][DOGNAME_INDEX]);
  entry.CarName.push(file[i][CARNAME_INDEX]);
  entry.LastName = file[i][LASTNAME_INDEX];
  ...
}

// and now to convert this into an array
let array = Object.keys(results).map(key => results[key]);


// retrieves or creates an entry for a given primary key
function getEntry(results, id) {
  return results[id] || (results[id] = {});
}

You could also get fancier and dynamically determine what the column indexes are, but the way I have it just keeps things simple.

Upvotes: 3

Related Questions