Converting parent / children structure to object - javascript

I need to convert js object like this:
{
"id": 1,
"name": "A",
"parent": null,
"children": [
{
"id": 2,
"name": "B",
"parent": 1,
"children": []
},
{
"id": 3,
"name": "C",
"parent": 1,
"children": [
{
"id": 4,
"name": "D",
"parent": 3,
"children": []
}
]
}
]
}
to another like this:
{
"А": [
{
"B": "value",
"C": [
{
"D": "value"
}
]
}
]
}
I wrote the function, but it returns the wrong object with several nested arrays:
const convert = (obj) => {
return obj.map((i) => {
return Object.keys(i).filter(y => y === 'name').map(key => {
return i['children'].length > 0 ? { [i[key]]: convert(i['children']) } : { [i[key]]: 'value' }
});
})
};
How to change my implementation for getting the right object?

You could build the entries from objects and map nested children, if exists.
const
transform = array => Object.fromEntries(array.map(({ name, children }) =>
[name, children.length ? [transform(children)] : 'value']
)),
data = { id: 1, name: "A", parent: null, children: [{ id: 2, name: "B", parent: 1, children: [] }, { id: 3, name: "C", parent: 1, children: [{ id: 4, name: "D", parent: 3, children: [] }] }] },
result = transform([data]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

how to change the format of json array by loping over

Hi I am getting data from API but I want my data in different format so that I can pass later into a function. I want to change the names of keys into a different one becasuse I have created a chart and it only draws if I send it data in certain way
This is what I am getting from API
data = {
"status": "success",
"from": "DB",
"indice": "KSE100",
"data": [
{
"stock_sector_name": "Tc",
"sector_score": "0828",
"stocks": [
{
"stock_symbol": "TRG",
"stock_score": 44
},
{
"stock_symbol": "SYS",
"stock_score": 33
}
]
},
{
"stock_sector_name": "OIL",
"sector_score": "0828",
"stocks": [
{
"stock_symbol": "FFS",
"stock_score": 44
},
{
"stock_symbol": "SMS",
"stock_score": 33
}
]
},
]
}
But I want my data to look like this like this
data = {
"name": "KSE100",
"children": [
{
"name": "A",
'points': -9,
"children": [
{
"stock_title": "A",
"value": 12,
},
{
"stock_title": "B",
"value": 4,
},
]
},
{
"name": "B",
'points': 20,
"children": [
{
"stock_title": "A",
"value": 12,
},
{
"name": "B",
"value": 4,
},
]
},
]
}
Like I want to replace
stock_sector_name = name
sector_score = value
stocks = children
stock_symbol = name
stock_score = value
I have been trying this for so much time but sill could not figured it out
function convert(d){
return {
name : d.indice,
children : d.data.map(y=>{
return {
name : y.stock_sector_name,
points : y.sector_score,
children : y.stocks.map(z=>{
return {
stock_title: z.stock_symbol,
value : z.stock_score
}
})
}
})
}
}
You can do something like this
const data = {
"status": "success",
"from": "DB",
"indice": "KSE100",
"data": [{
"stock_sector_name": "Tc",
"sector_score": "0828",
"stocks": [{
"stock_symbol": "TRG",
"stock_score": 44
},
{
"stock_symbol": "SYS",
"stock_score": 33
}
]
},
{
"stock_sector_name": "OIL",
"sector_score": "0828",
"stocks": [{
"stock_symbol": "FFS",
"stock_score": 44
},
{
"stock_symbol": "SMS",
"stock_score": 33
}
]
},
]
}
const data2 = {
"name": "KSE100",
"children": [{
"name": "A",
'points': -9,
"children": [{
"stock_title": "A",
"value": 12,
},
{
"stock_title": "B",
"value": 4,
},
]
},
{
"name": "B",
'points': 20,
"children": [{
"stock_title": "A",
"value": 12,
},
{
"name": "B",
"value": 4,
},
]
},
]
}
//stock_sector_name = name
//sector_score = value
//stocks = children
//stock_symbol = stock_title
//stock_score = value
const keys = {
stock_sector_name: "name",
sector_score: "points",
stocks: "children",
stock_symbol: "stock_title",
stock_score: "value",
indice: "name",
//data: "children"
}
const rename = (value) => {
if (!value || typeof value !== 'object') return value;
if (Array.isArray(value)) return value.map(rename);
return Object.fromEntries(Object
.entries(value)
.map(([k, v]) => [keys[k] || k, rename(v)])
);
}
renamedObj = rename(data);
console.log(renamedObj);

Sort Array of Objects Childs to new Array with Objects

I'm programming a small vue.js App and need to convert an array to a new one and sort them.
The array of objects I get from the backend server looks like that:
var arr =
[
{
"id": 1,
"name": "Name1",
"parents": {
"someOtherTings": "Test",
"partentOfParent": {
"mainId": 10
}
}
},
{
"id": 2,
"name": "Name2",
"parents": {
"someOtherTings": "Test",
"partentOfParent": {
"mainId": 11
}
}
},
{
"id": 3,
"name": "Name3",
"parents": {
"someOtherTings": "Test",
"partentOfParent": {
"mainId": 10
}
}
}
]
console.log(arr)
But I need a new array, that is sorted like that:
var newArr =
[
{
"mainId": 10,
"parents": {
"id": 1,
"name": "Name1"
}
},
{
"mainId": 11,
"parents": [
{
"id": 2,
"name": "Name2"
},
{
"id": 3,
"name": "Name3"
}
]
}
]
What is the best way to implement this?
You could group the items with the help of a Map.
var array = [{ id: 1, name: "Name1", parents: { someOtherTings: "Test", partentOfParent: { mainId: 10 } } }, { id: 2, name: "Name2", parents: { someOtherTings: "Test", partentOfParent: { mainId: 11 } } }, { id: 3, name: "Name3", parents: { someOtherTings: "Test", partentOfParent: { mainId: 10 } } }],
result = Array.from(
array.reduce(
(m, { id, name, parents: { partentOfParent: { mainId } } }) =>
m.set(mainId, [...(m.get(mainId) || []), { id, name }]),
new Map
),
([mainId, parents]) => ({ mainId, parents })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You just need a combination of map to create a new array and then sort it based on the mainId value
var arr = [{
"id": 1,
"name": "Name1",
"parents": {
"someOtherTings": "Test",
"partentOfParent": {
"mainId": 10
}
}
},
{
"id": 2,
"name": "Name2",
"parents": {
"someOtherTings": "Test",
"partentOfParent": {
"mainId": 11
}
}
},
{
"id": 3,
"name": "Name3",
"parents": {
"someOtherTings": "Test",
"partentOfParent": {
"mainId": 10
}
}
}
]
const newArr = arr.map(obj => ({
mainId: obj.parents.partentOfParent.mainId,
parents: {
id: obj.id,
name: obj.name
},
})).sort((a, b) => b - a);
console.log(newArr);

How to find the parent JSON object vaue using the child object value dynamically in Javascript?

var obj = [
{
"name": "A1",
"children": [
{
"name": "A1-level1-child1",
"children": [
{
"name": "A1-level2-child1",
"children": [
{
"name": "A1-level3-child1",
"children": []
},
{
"name": "A1-level3-child2",
"children": []
}
]
}
]
},
{
"name": "A2-level1-child1",
"children": []
}
]
},
{
"name": "B1",
"children": [
]
}
];
From the above JSON object, if i check the value "A1-level3-child1", the function should give me its parent name as "A1-level2-child1". Same way, if i check for "A2-level1-child1",then it should be give me the parent value as "A1".
You could iterate the array or children and use a short circuit if the node is found.
function getParentName(array, name, parent = 'root') {
var result;
array.some(o => result = o.name === name && parent
|| o.children && getParentName(o.children, name, o.name));
return result;
}
var array = [{ name: "A1", children: [{ name: "A1-level1-child1", children: [{ name: "A1-level2-child1", children: [{ name: "A1-level3-child1", children: [] }, { name: "A1-level3-child2", children: [] }] }] }, { name: "A2-level1-child1", children: [] }] }, { name: "B1", children: [] }];
console.log(getParentName(array, "A1-level3-child1")); // A1-level2-child1
console.log(getParentName(array, "A2-level1-child1")); // A1
GoGo this code.
var parentMap = {}
function getParentMap(arr, parent) {
if (!(arr instanceof Array)) {
return;
}
for (o of arr) {
parentMap[o.name] = parent;
if (o.children && o.children.length) {
getParentMap(o.children, o);
//getParentMap(o.children, o.name);
}
}
}
var arr = [{
"name": "A1",
"children": [{
"name": "A1-level1-child1",
"children": [{
"name": "A1-level2-child1",
"children": [{
"name": "A1-level3-child1",
"children": []
},
{
"name": "A1-level3-child2",
"children": []
}
]
}]
},
{
"name": "A2-level1-child1",
"children": []
}
]
},
{
"name": "B1",
"children": []
}];
getParentMap(obj, null);
parentMap["A1-level3-child1"].name
If you can redefine this structure, you can add 'parent' on every node, so more easy to operate it.

Construct flat array from tree of objects

Suppose I have a tree of objects like the following, perhaps created using the excellent algorithm found here: https://stackoverflow.com/a/22367819/3123195
{
"children": [{
"id": 1,
"title": "home",
"parent": null,
"children": []
}, {
"id": 2,
"title": "about",
"parent": null,
"children": [{
"id": 3,
"title": "team",
"parent": 2,
"children": []
}, {
"id": 4,
"title": "company",
"parent": 2,
"children": []
}]
}]
}
(Specifically in this example, the array returned by that function is nested as the children array property inside an otherwise empty object.)
How would I convert it back to a flat array?
Hope your are familiar with es6:
let flatten = (children, extractChildren) => Array.prototype.concat.apply(
children,
children.map(x => flatten(extractChildren(x) || [], extractChildren))
);
let extractChildren = x => x.children;
let flat = flatten(extractChildren(treeStructure), extractChildren)
.map(x => delete x.children && x);
UPD:
Sorry, haven't noticed that you need to set parent and level. Please find the new function below:
let flatten = (children, getChildren, level, parent) => Array.prototype.concat.apply(
children.map(x => ({ ...x, level: level || 1, parent: parent || null })),
children.map(x => flatten(getChildren(x) || [], getChildren, (level || 1) + 1, x.id))
);
https://jsbin.com/socono/edit?js,console
This function will do the job, plus it adds a level indicator to each object. Immediate children of treeObj will be level 1, their children will be level 2, etc. The parent properties are updated as well.
function flatten(treeObj, idAttr, parentAttr, childrenAttr, levelAttr) {
if (!idAttr) idAttr = 'id';
if (!parentAttr) parentAttr = 'parent';
if (!childrenAttr) childrenAttr = 'children';
if (!levelAttr) levelAttr = 'level';
function flattenChild(childObj, parentId, level) {
var array = [];
var childCopy = angular.extend({}, childObj);
childCopy[levelAttr] = level;
childCopy[parentAttr] = parentId;
delete childCopy[childrenAttr];
array.push(childCopy);
array = array.concat(processChildren(childObj, level));
return array;
};
function processChildren(obj, level) {
if (!level) level = 0;
var array = [];
obj[childrenAttr].forEach(function(childObj) {
array = array.concat(flattenChild(childObj, obj[idAttr], level+1));
});
return array;
};
var result = processChildren(treeObj);
return result;
};
This solution takes advantage of Angular's angular.extend() function to perform a copy of the child object. Wiring this up with any other library's equivalent method or a native function should be a trivial change.
The output given for the above example would be:
[{
"id": 1,
"title": "home",
"parent": null,
"level": 1
}, {
"id": 2,
"title": "about",
"parent": null,
"level": 1
}, {
"id": 3,
"title": "team",
"parent": 2,
"level": 2
}, {
"id": 4,
"title": "company",
"parent": 2,
"level": 2
}]
It is also worth noting that this function does not guarantee the array will be ordered by id; it will be based on the order in which the individual objects were encountered during the operation.
Fiddle!
Here it goes my contribution:
function flatNestedList(nestedList, childrenName, parentPropertyName, idName, newFlatList, parentId) {
if (newFlatList.length === 0)
newFlatList = [];
$.each(nestedList, function (i, item) {
item[parentPropertyName] = parentId;
newFlatList.push(item);
if (item[childrenName] && item[childrenName].length > 0) {
//each level
flatNestedList(item[childrenName], childrenName, parentPropertyName, idName, newFlatList, item[idName]);
}
});
for (var i in newFlatList)
delete (newFlatList[i][childrenName]);
}
Try following this only assumes each item is having children property
class TreeStructureHelper {
public toArray(nodes: any[], arr: any[]) {
if (!nodes) {
return [];
}
if (!arr) {
arr = [];
}
for (var i = 0; i < nodes.length; i++) {
arr.push(nodes[i]);
this.toArray(nodes[i].children, arr);
}
return arr;
}
}
Usage
let treeNode =
{
children: [{
id: 1,
title: "home",
parent: null,
children: []
}, {
id: 2,
title: "about",
parent: null,
children: [{
id: 3,
title: "team",
parent: 2,
children: []
}, {
id: 4,
title: "company",
parent: 2,
children: []
}]
}]
};
let flattenArray = _treeStructureHelper.toArray([treeNode], []);
This is data:
const data = {
id: '1',
children: [
{
id: '2',
children: [
{
id: '4',
children: [
{
id: '5'
},
{
id: '6'
}
]
},
{
id: '7'
}
]
},
{
id: '3',
children: [
{
id: '8'
},
{
id: '9'
}
]
}
]
}
In React.JS just declare an array field in state and push items to that array.
const getAllItemsPerChildren = item => {
array.push(item);
if (item.children) {
return item.children.map(i => getAllItemsPerChildren(i));
}
}
After function call your array in state will hold all items as below:
One more 😄😁
function flatten(root, parent=null, depth=0, key='id', flat=[], pick=() => {}) {
flat.push({
parent,
[key]: root[key],
depth: depth++,
...pick(root, parent, depth, key, flat)
});
if(Array.isArray(root.children)) {
root.children.forEach(child => flatten(child, root[key], depth, key, flat, pick));
}
}
let sample = {
"id": 0,
"children": [{
"id": 1,
"title": "home",
"parent": null,
"children": []
}, {
"id": 2,
"title": "about",
"parent": null,
"children": [{
"id": 3,
"title": "team",
"parent": 2,
"children": []
}, {
"id": 4,
"title": "company",
"parent": 2,
"children": []
}]
}]
};
let flat = [];
flatten(sample, null, 0, 'id', flat, root => ({ title: root.title }));
let expected = [
{
"id": 0,
"parent": null,
"depth": 0
},
{
"id": 1,
"parent": 0,
"depth": 1,
"title": "home"
},
{
"id": 2,
"parent": 0,
"depth": 1,
"title": "about"
},
{
"id": 3,
"parent": 2,
"depth": 2,
"title": "team"
},
{
"id": 4,
"parent": 2,
"depth": 2,
"title": "company"
}
];

Linq.Js Group By with Count

I have the following array:
var data= [{ "Id": 1, "Name": "NameOne"}
{ "Id": 2, "Name": "NameTwo"}
{ "Id": 2, "Name": "NameTwo"}]
{ "Id": 3, "Name": "NameThree"}]
Using linq.js I would like to return the following array:
var data= [{ "Id": 1, "Name": "NameOne", Total: 1}
{ "Id": 2, "Name": "NameTwo", Total: 2}
{ "Id": 3, "Name": "NameThree", Total: 1}]
This means that I need to use GroupBy() with a Count(). I am not sure how to apply this using the linq.js reference.
It's simple really:
var data = [
{ Id: 1, Name: 'NameOne' },
{ Id: 2, Name: 'NameTwo' },
{ Id: 2, Name: 'NameTwo' },
{ Id: 3, Name: 'NameThree' }
];
var query = Enumerable.From(data)
// GroupBy (keySelector, elementSelector, resultSelector, compareSelector)
.GroupBy(
null, // (identity)
null, // (identity)
"{ Id: $.Id, Name: $.Name, Total: $$.Count() }",
"'' + $.Id + '-' + $.Name"
)
.ToArray();
Use the overload of GroupBy() that includes the resultSelector, you'll want to grab the count of the grouped items (the second parameter).
You were probably having issues with the data not being uniform. a reduce flattens your data structure, and then you can manipulate it as you wish in the .Select().
var intialData = [[{ "Id": 1, "Name": "NameOne"}, { "Id": 2, "Name": "NameTwo"}, { "Id": 2, "Name": "NameTwo"}], { "Id": 3, "Name": "NameThree"}];
var data = Enumerable.From(intialData.reduce(function(a,b) { return a.concat(b); }))
.GroupBy(function(item) { return item.Id; })
.Select(function(item) { return {"Id":item.source[0].Id, "Name":item.source[0].Name, "Total": item.source.length}; })
.ToArray();

Categories