Restructure / rebuild json data tree - javascript

{
"company": [
{ "region": [ "Europe", "Germany" ], "productLine": "Produce" },
{ "region": [ "Europe", "France" ], "productLine": "Produce" }
],
"company2": [
{ "region": [ "Europe", "Germany" ], "productLine": "Produce" },
{ "region": [ "Americas", "USA" ], "productLine": "Produce" }
]
}
With this json data how can I rebuild it so that I have Europe/Americas value as the primary(unique) node with Germany/France as it's children? company/company1 would be sub-children of France/Germany. I cant seem to figure out how to build arrays while keeping the relations correct. I essence I need to reverse the node tree.
Expected Output:
Tree structure like this:
-Europe
-France
-Company
-Company2
I also need a special structure for a tree plugin:
var source = [ { label: "Europe", items: [
{label: "France", items: [
{label: "SuperShop", items: [
{label: "Produce"}
]}
]
}]
}]
What I need in the end is an Object array with value pair: label, items. Items being an object with sub-objects within.

Obviously, I don't know why you need the new format, but it seems overly complex. If you have a large data set that you are looking through, you are going to take a hit on speed because, under it's current set up, you are going to have traverse over every element of the new array to find the one you are looking for ...
var inputs = {
"company": [
{ "region": [ "Europe", "Germany" ], "productLine": "Produce" },
{ "region": [ "Europe", "France" ], "productLine": "Produce" }
],
"company2": [
{ "region": [ "Europe", "Germany" ], "productLine": "Produce" },
{ "region": [ "Americas", "USA" ], "productLine": "Produce" }
]
};
var converter = {};
// This new format requires a 2 step process to prevent it from being N^2
// So convert the input into a tree
// Region
// -> Country
// -> Company
// -> Array of Products
for(var company in inputs){
for(var i = 0; i < inputs[company].length; i++){
// Because the regions are an array of hashes it is simplest
// to grab the value by using the previously gathered keys
// and the key region
var r = inputs[company][i]['region'];
// Check if the region exists. If not create it.
if(!converter[r[0]]){
converter[r[0]] = {};
}
// Check if the country exists. If not create it.
if(!converter[r[0]][r[1]]){
converter[r[0]][r[1]] = {};
}
// Add the company to the array.
if(!converter[r[0]][r[1]][company]){
converter[r[0]][r[1]][company] = [];
}
converter[r[0]][r[1]][company].push(inputs[company][i]['productLine']);
}
}
var outputs = [];
// Now walk converter and generate the desired object.
for( var region in converter){
converted_region = {};
converted_region["label"] = region;
converted_region["items"] = [];
for( var country in converter[region]){
converted_country = {};
converted_country["label"] = country;
converted_country["items"] = [];
for( var company in converter[region][country]){
converted_company = {};
converted_company["label"] = company;
converted_company["items"] = [];
for(var i = 0; i < converter[region][country][company].length; i++){
converted_company["items"].push(converter[region][country][company][i]);
}
converted_country["items"].push(converted_company);
}
converted_region["items"].push(converted_country);
}
outputs.push(converted_region);
}

Related

How to update object based off objects specific value?

I have 2 objects and I want to 'transplant' values from one object into the other.
The first object I am drawing data from looks like:
var userData = {
Data: [
{
Amount: 430140.68,
Year: "2015",
AccountName: "Account 1"
},
{
Amount: 458997.32,
Year: "2016",
Name: "Account 2"
},
]
}
The 2nd object I am placing data into looks like:
[
{
"name": "Account 1",
"data": [
0,
0
],
},
{
"name": "Account 2",
"data": [
0,
0
],
}
]
My goal is to take the Amount form the first object and place it in the data array of the 2nd. Each year corresponds to a value in the 'data` array.
So, the resulting updated object should look like:
[
{
"name": "Account 1",
"data": [
430140.68,
0
],
},
{
"name": "Account 2",
"data": [
0,
458997.32
],
}
]
To try to achieve this I have the following code:
const yearArrLength = yearsArr.length;
const generatedObj = new Array(yearArrLength).fill(0);
// Push name and populate data array with 0s.
for (var key of Object.keys(userData.Data)) {
var accName = userData.Data[key].AccountName;
if (!generatedObj.find(key => key.name === accName)){
generatedObj.push({'name': accName, 'data': blankDataArr});
}
}
for (var key of Object.keys(userData.Data)) {
var accName = userData.Data[key].AccountName;
var accAmount = userData.Data[key].Amount;
var accYear = userData.Data[key].Year;
// Get location of years array value
var yearArrIndex = yearsArr.indexOf(accYear);
for (var key of Object.keys(generatedObj)) {
if (generatedObj[key].name == accName) {
generatedObj[key].data[yearArrIndex] = accAmount;
}
}
}
However, this seems to populate all of the data array values, eg:
[
{
"name": "Account 1",
"data": [
430140.68,
458997.32
],
},
{
"name": "Account 2",
"data": [
430140.68,
458997.32
],
}
]
I'm completely stumped as to why. The if statement should be checking if there is a matching account name, but it doesn't seem to fire.
Would anyone know what I've done wrong?
It looks like you're pushing the exact same blankDataArr each time - you're not pushing a new array, you're pushing the same array to all.
For a more minimal example:
const subarr = [];
const arr = [subarr, subarr];
arr[0].push('x');
console.log(JSON.stringify(arr));
// both items in `arr` have changed
// because both refer to the exact same subarr object
For what you're trying to do, it looks like it'd be a lot easier to make an object or Map indexed by AccountName first, that way you just have to access or create the AccountName property while iterating, and assign to the appropriate year.
const yearsArr = ['2015', '2016'];
const userData = {
Data: [
{
Amount: 430140.68,
Year: "2015",
AccountName: "Account 1"
},
{
Amount: 458997.32,
Year: "2016",
AccountName: "Account 2"
},
]
};
const dataByAccountName = new Map();
for (const { AccountName, Amount, Year } of userData.Data) {
if (!dataByAccountName.has(AccountName)) {
// Create an entirely new array:
dataByAccountName.set(AccountName, yearsArr.map(() => 0));
}
const index = yearsArr.indexOf(Year);
dataByAccountName.get(AccountName)[index] = Amount;
}
const result = [...dataByAccountName.entries()].map(([name, data]) => ({ name, data }));
console.log(result);

Filter on two arrays same time?

I have two arrays:
const array1 = [{
"id": "4521",
"name": "Tiruchirapalli",
"stateId": "101"
},
{
"id": "1850",
"name": "Tenkasi",
"stateId": "101"
},
{
"id": "202",
"name": "Thanjavur",
"stateId": "101"
},
{
"id": "505",
"name": "Ernakulam",
"stateId": "102"
},
];
And now array2
const array2 = [{
"id": 1850,
"cityName": "Tenkasi",
"aliasNames": [
"Thenkasi"
]
},
{
"id": 4521,
"cityName": "Tiruchirapalli",
"aliasNames": [
"Trichy"
]
},
{
"id": 202,
"cityName": "Thanjavur",
"aliasNames": [
"Tanjore"
]
},
{
"id": 505,
"cityName": "Ernakulam",
"aliasNames": [
"Kochi",
"Cochin"
]
},
];
what i need to do is, how to filter both the arrays at same time ( or filter first one and then second which ever one is performance effective ).
For instance, when user types "Kochi", first it should check on array1 to find if its has name="Kochi", if it has then we can set the state with that and if it doesnt have we need to find it on array2 and the update the state !
Which is fast and effective way to handle this - ( array1 has 2500 records and array2 has 990 records ) so performance / speed is also a concern
My attempt:
searchFilterFunction = text => {
this.setState({ typedText: text });
const newData = array1.filter(item => {
const itemData = `${item.name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({ data: newData});
};
How to implement the second filter in optimized way ?
For instance, when user types "Kochi", first it should check on array1
to find if its has name="Kochi", if it has then we can set the state
with that and if it doesnt have we need to find it on array2 and the
update the state !
I would do something like this with Array.find.
if( array1.find(item=>item.name.toUpperCase() === text) ) {
// set state
} else if( array2.find(item=>item.cityName.toUpperCase() === text) ) {
// set state
}
A refined form would be
let result = array1.find(item=>item.name.toUpperCase() === text);
// check in array 2 as we cannot find in array 1
if(!result) {
result = array2.find(item=>{
// check in aliasNames and in cityName
return item.cityName.toUpperCase() === text || item.aliasNames.includes(text);
}
);
}
if(result) {
setState(result);
} else {
// place not found
}
Regarding the performance based on your array count you will not see much difference. If you want to save some milliseconds you can check the array with least count first as mentioned in one of the comments. But the time also varies based on were the element is in array.
I think this is the most optimal solution because nesting the two filter won't work as you need to filter from first array and then second.
const array1 = [{
"id": "4521",
"name": "Tiruchirapalli",
"stateId": "101"
},
{
"id": "1850",
"name": "Tenkasi",
"stateId": "101"
},
{
"id": "202",
"name": "Thanjavur",
"stateId": "101"
},
{
"id": "505",
"name": "Ernakulam",
"stateId": "102"
},
];
const array2 = [{ "id": 1850, "cityName": "Tenkasi",
"aliasNames": [
"Thenkasi"
]
},{"id": 4521,"cityName": "Tiruchirapalli",
"aliasNames": [
"Trichy"
]
},
{
"id": 202,
"cityName": "Thanjavur",
"aliasNames": [
"Tanjore"
]
},
{
"id": 505,
"cityName": "Ernakulam",
"aliasNames": [
"Kochi",
"Cochin"
]
},
];
function filter(text) {
// Complexity Linear
const filter_array = array1.filter((a) => {
return (a.name === text)
});
if (filter_array.length > 0) {
//Set State and return
}
//Complexity Linear and includes complexity Linear O(sq(m*n)) where n is //the aliasName record
const filter_array2 = array2.filter((a) => {
return a.cityName === text || a.aliasNames.includes(text);
});
return filter_array2 //Set State filter array 2
}
console.log(filter("Kochi"));

Javascript -sort array based on another javascript object properties

I have one javascript array and one object . Need help to sort javascript object keys based on the order number in another array
In subgroup array , I have name , order number. Need to sort Offerings keys based on that order number
const subgroup = [
{
"code": "6748",
"name": "test123",
"orderNumber": "0"
},
{
"code": "1234",
"name": "customdata",
"orderNumber": "1"
}
]
const offerings = {
"customdata" : [
{
"code": "Audi",
"color": "black"
}
],
"test123" : [
{
"brand": "Audi",
"color": "black"
}
]
}
I believe this should work for you. I've added some comments in the code that should hopefully do an okay job of explaining what is happening.
var subgroup = [{
"code": "6748",
"name": "test123",
"orderNumber": "0"
}, {
"code": "1234",
"name": "customdata",
"orderNumber": "1"
}];
var offerings = {
"customdata": [{
"code": "Audi",
"color": "black"
}],
"test123": [{
"brand": "Audi",
"color": "black"
}]
}
function sortObjectFromArray(refArray, sortObject, orderKey = 'order', linkKey = 'key') {
// Get copy of refArray
let reference = refArray.slice();
// Sort sortObject [ into an array at this point ]
let sorted = [];
for (let key in sortObject) {
// Searches the refArray for the linkKey, and returns the intended index
let index = reference.find((item) => item[linkKey] === key)[orderKey];
// Places the sortObject's value in the correct index of the 'sorted' Array
sorted[parseInt(index)] = [key, sortObject[key]];
};
// Return an object, created from previous 'sorted' Array
return sorted.reduce((obj, [key, value]) => {
obj[key] = value;
return obj;
}, {});
};
offerings = sortObjectFromArray(subgroup, offerings, 'orderNumber', 'name');
console.log(offerings);

Count object property inside array using lodash or vanilla javascript

I have this object with nested arrays/objects:
{
"USA": [
{
"location": "New York",
"municipality": "Manhattan",
},
{
"location": "Texas",
"municipality": "Austin",
}
],
"CANADA": [
{
"location": "Ontario",
"municipality": "no municipality",
}
]
}
I want to use lodash or plain javascript to count how many location are inside the USA and CANADA. How is that possible?
desired result:
USA: 2
CANADA: 1
Just use the array lengths:
var USA = myObj.USA.length;
var Canada = myObj.CANADA.length;
Or, for larger data sets:
var result = {};
Object.keys(myObj)
.forEach(function(key,index) {
result[key] = myObj[key].length;
});
With lodash you could use mapValues:
let result = _.mapValues(data, 'length');
The solution using Array.prototype.reduce() function:
var obj = {
"USA": [ { "location": "New York", "municipality": "Manhattan" }, { "location": "Texas", "municipality": "Austin" } ], "CANADA": [ { "location": "Ontario", "municipality": "no municipality" }]
},
result = Object.keys(obj).reduce(function(r,k){
r[k] = obj[k].length;
return r;
}, {});
console.log(result)

Reformat json data using Jquery

I need to convert the following JSON data:
var response = {
"data": {
"List": [
{
"Name": "Mary",
"state": "AZ",
"Matriculation": "complete",
"Graduation": "complete",
"Masters": "complete",
"Phd": "notStarted"
},
{
"Name": "Stephanie",
"state": "CT",
"Matriculation": "complete",
"Graduation": "complete",
"Masters": "complete",
"Phd": "notStarted"
},
{
"Name": "John",
"state": "CT",
"Matriculation": "complete",
"Graduation": "planning",
"Masters": "notStarted",
"Phd": "notStarted"
}]
}
}
into the following using jQuery:
[
{
"state":"AZ",
"Matriculation":[
{
"Name":"Mary",
"status":"complete"
}
],
"Graduation":[
{
"Name":"Mary",
"status":"complete"
}
],
"Masters":[
{
"Name":"Mary",
"status":"complete"
}
],
"Phd":[
{
"Name":"Mary",
"status":"notStarted"
}
]
},
{
"state":"CT",
"Matriculation":[
{
"Name":"Stephanie",
"status":"complete"
},
{
"Name":"John",
"status":"complete"
}
],
"Graduation":[
{
"Name":"Stephanie",
"status":"complete"
},
{
"Name":"John",
"status":"planning"
}
],
"Masters":[
{
"Name":"Stephanie",
"status":"complete"
},
{
"Name":"John",
"status":"notStarted"
}
],
"Phd":[
{
"Name":"Stephanie",
"status":"notStarted"
},
{
"Name":"John",
"status":"notStarted"
}
]
}
]
This is what I have tried so far with zero progress.
I tried to accomplish this for one state first.
This is the fiddle:
http://jsfiddle.net/sqgdyk6f/
Any guidance is appreciated.
I am new to JSON manipulation.
Thanks in advance!
First thing you should do is analyze your source format, and your destination format.
Your source format is just an array of objects of individual people.
Your destination format is an object that groups the people by state
and into certain categories.
Next, think about how you can get the source into this destination.
You're going to want to iterate over each individual person in the source, and try to add it to your array of states. Now, I'll say your destination format is a bit peculiar, but by writing a couple of helper functions, you can achieve that format.
Take a look at your destination format again. It's an array of objects. Part of each object in that array is a property called state that has a value for the state that object represents. You're going to want a function that can look through this array for a specific state, and return the existing object for you. If the state doesn't exist, it should add a new entry to the array for you and return that.
Since JSON just means JavaScript Object Notation, and you are actually working with JavaScript objects, you should create an object that models each entry of your destination array. I'm going to call these StateSummary.
function StateSummary(state) {
this.state = state;
this.Matriculation = [];
this.Graduation = [];
this.Masters = [];
this.Phd = [];
}
Can you see how this object represents each entry in your destination array?
Now that you have an object that models each entry, we need a function that can check to see if an entry for a certain state already exists. If it exists, it will return that object. If it doesn't exist, it will add a new object to the array, and return this new object.
function addOrGetState(array, state) {
for (var i = 0; i < array.length; i++) {
var obj = array[i];
if (obj.state === state)
return obj;
}
//If the function got here, then it didn't find a matching state.
var obj = new StateSummary(state);
array.push(obj); //Add the new object to the array.
return obj; //Return the new object.
}
So, now you can get an entry by state from your destination array, and you can create new entries.
Go ahead and create a function that will add a person to a StateSummary object.
function addPersonToStateSummary(person, stateSummary) {
stateSummary.Matriculation.push({ Name: person.Name, status: person.Matriculation });
stateSummary.Graduation.push({ Name: person.Name, status: person.Graduation});
stateSummary.Masters.push({ Name: person.Name, status: person.Masters});
stateSummary.Phd.push({ Name: person.Name, status: person.Phd});
}
The last piece is iterating over the source array, and massaging that data into your destination array.
var sourceArray = response.data.List; //You provided this.
var destinationArray = []; //Allocate a new array to put stuff in.
for (var i = 0; i < sourceArray.length; i++) {
var person = sourceArray[i]; //Each entry of the source array represents a person.
var stateSummary = addOrGetState(destinationArray, person.state);
addPersonToStateSummary(person, stateSummary);
}
This should give you what you are looking for. I hope this breakdown teaches you how to think about the problem in an analytical way, breaking its steps down first, and then solving them with code.
Here is a demo:
var response = {
"data": {
"List": [{
"Name": "Mary",
"state": "AZ",
"Matriculation": "complete",
"Graduation": "complete",
"Masters": "complete",
"Phd": "notStarted"
}, {
"Name": "Stephanie",
"state": "CT",
"Matriculation": "complete",
"Graduation": "complete",
"Masters": "complete",
"Phd": "notStarted"
}, {
"Name": "John",
"state": "CT",
"Matriculation": "complete",
"Graduation": "planning",
"Masters": "notStarted",
"Phd": "notStarted"
}]
}
};
function StateSummary(state) {
this.state = state;
this.Matriculation = [];
this.Graduation = [];
this.Masters = [];
this.Phd = [];
}
function addOrGetState(array, state) {
for (var i = 0; i < array.length; i++) {
var obj = array[i];
if (obj.state === state)
return obj;
}
//If the function got here, then it didn't find a matching state.
var obj = new StateSummary(state);
array.push(obj); //Add the new object to the array.
return obj; //Return the new object.
}
function addPersonToStateSummary(person, stateSummary) {
stateSummary.Matriculation.push({
Name: person.Name,
status: person.Matriculation
});
stateSummary.Graduation.push({
Name: person.Name,
status: person.Graduation
});
stateSummary.Masters.push({
Name: person.Name,
status: person.Masters
});
stateSummary.Phd.push({
Name: person.Name,
status: person.Phd
});
}
var sourceArray = response.data.List; //You provided this.
var destinationArray = []; //Allocate a new array to put stuff in.
for (var i = 0; i < sourceArray.length; i++) {
var person = sourceArray[i]; //Each entry of the source array represents a person.
var stateSummary = addOrGetState(destinationArray, person.state);
addPersonToStateSummary(person, stateSummary);
}
document.getElementById('result').innerHTML = JSON.stringify(destinationArray);
<div id="result"></div>

Categories

Resources