Javascript - Recursive object manipulation - javascript

I'm having some difficulties with recursive functions. Can someone help me?
I have the following structure:
{
"entity": {
"entityLabel": "Virtual Reality",
"parent": [
{
"entity": {
"entityLabel": "Artificial Intelligence",
"parent": [
{
"entity": {
"entityLabel": "Information Technology"
}
}
]
}
}
]
}
}
And I need the following result:
{
"label": "Information Technology",
"children": [
{
"label": "Artificial Intelligence"
"children": [
{
label: "Virtual Reality"
}
]
}
]
}
I couldn't accomplish the reverse order. My current code is:
const termTree = term => {
const label = term.entity?.entityLabel
const parentArr = term.entity?.parent
const obj = {}
let children = []
if (parentArr) {
children = parentArr.map(item => {
return termTree(item)
})
}
obj.label = label
if (!empty(children)) obj.children = children
return obj
}
Which results in the same order but with different labels:
{
"label": "Virtual Reality",
"children": [
{
"label": "Artificial Intelligence",
"children": [
{
"label": "Information Technology"
}
]
}
]
}
As you can see it's reverse and it's not just a matter of changing labels.
Thanks

You could take an iterative and recursive approach by handing over a source array for the new label and a target array for the final result with children.
By taking the above wanted format, without a children property in the most inner object, this approach take as target an object with a children. The reducing part for generating the new data structure takes only objects as return value and creates children if necessary.
var data = { entity: { entityLabel: "Virtual Reality", parent: [{ entity: { entityLabel: "Artificial Intelligence", parent: [{ entity: { entityLabel: "Information Technology" } }] } }] } },
result = [];
[data].forEach(function iter(source, target) {
return function ({ entity: { entityLabel, parent } }) {
source = [entityLabel, ...source];
if (parent) return parent.forEach(iter(source, target));
source.reduce((t, label) => {
var temp = (t.children = t.children || []).find(o => o.label === label);
if (!temp) {
t.children.push(temp = { label });
}
return temp;
}, target);
}
}([], { children: result }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

frame array of recursive json object from an another array of objects

i have an array of objects of the below format
each with a unique 'sub-task' entry, each of this sub-task is to be embedded as a children element of each unique 'task' from the 'tasks' array
[
{
"sub-task":"abc",
"task":"alpha1"},
{
"sub-task":"def",
"task":"alpha1"},
{
"sub-task":"ijkl",
"task":"proto"},
{
"sub-task":"mno",
"task":"def"},
{
"sub-task":"qrs",
"task":"proto"},
{
"sub-task":"asdf",
"task":"mno"},
]
i was trying to frame an another array of below format
[
{
"name":"alpha1",
"children":[
{
"name":"abc"
},
{
"name":"def",
"children":[
{
"name":"mno"
}
]
}
]
},
{
"name":"proto",
"children":[
{
"name":"ijkl"
},
{
"name":"qrs",
"children":[
{
"name":"asdf"
}
]
}
]
}
]
i was trying of below logic, but ended up with no solution...
var treeData = [];
for( var ele of tasks){
recurOn(treeData,ele);
}
function recurOn(arr,obj){
if(arr.length == 0){
treeData.push({name:obj.parentGroup,children:[{name:obj.groupName}]})
//console.log(treeData);
return 1;
}else {
for(var item of treeData){
if(item.name == obj.parentGroup){
//console.log('item: ', item);
item.children.push({name:obj.groupName});
break;
}
else {
treeData.push(recurOn([],obj))
}
}
return 1;
}
}
//console.log(treeData);
//console.log(result);
Since the no of levels an elements holds is not known i was unable to fix for a logic
Use a map to store object reference.
let input = [
{ "sub-task": "abc", "task": "alpha1" },
{ "sub-task": "def", "task": "alpha1" },
{ "sub-task": "ijkl", "task": "proto" },
{ "sub-task": "mno", "task": "def" },
{ "sub-task": "qrs", "task": "proto" },
{ "sub-task": "asdf", "task": "mno" },
];
let map = new Map, result = [];
input.forEach(({ ["sub-task"]: name, task }) => {
let node = map.get(task), child = { name, children: [] };
if (!node) {
map.set(task, node = { name: task, children: [] });
result.push(node);
}
map.set(name, child);
node.children.push(child);
})
console.log(result);

How do I take an array of objects and reduce it so that data at a repeated object key is combined?

I'm working on a react app that mimics a retail website. My main page displays an item, and below has card components of related products. When I click a button on one of the related products, I open a comparison modal that compares features of the current product and the clicked upon product. I figured that to accomplish this, I would create an array of the combined features of the clicked on product and the main page product. I've been struggling to get create an array of objects, where each unique feature has an object with data inside about the features and which product the feature belongs to.
As of right now, I've been able to get an array of all the features that the two products have, but this array has repeats if the products have overlapping features. This makes me unsure of how to render the comparison table because I was planning on mapping over the array and creating a table row for each feature. My current code to format these features is as follows:
formatFeatures: (currentProd, clickedProd) => {
let combinedFeatures = [];
if (clickedProd.features) {
clickedProd.features.forEach(feature => {
let obj = {}
let vals = Object.values(feature);
obj[vals[0]] = [vals[1], clickedProd.id]
combinedFeatures.push(obj)
})
}
currentProd.features.forEach(feature => {
let obj = {}
let vals = Object.values(feature);
obj[vals[0]] = [vals[1], currentProd.id]
combinedFeatures.push(obj)
})
let formattedFeatures = combinedFeatures.reduce((allFeatures, feature) => {
if (Object.keys(feature) in allFeatures) {
allFeatures = [allFeatures[Object.keys(feature)]].concat(feature);
} else {
allFeatures.push(feature);
}
return allFeatures;
}, [])
The result of this is:
[{
"Fabric": ["100% Cotton", 28214]
}, {
"Cut": ["Skinny", 28214]
}, {
"Fabric": ["Canvas", 28212]
}, {
"Buttons": ["Brass", 28212]
}]
This is pretty close to what I am looking for, where I have an array of objects that contain information about the feature and product id of the product, but the repeat in "Fabric" is something I'm struggling to sort out. Ideally, the result would look like this:
[{
"Fabric": ["100% Cotton", 28214],
["Canvas", 28212]
}, {
"Cut": ["Skinny", 28214]
}, {
"Buttons": ["Brass", 28212]
}]
If anyone can help guide me as to how to change my formatting function to accomplish this, I'd be very grateful. Alternatively, if anyone knows a better way to dynamically format a table with a single row for each unique feature given my current result, that would be great too.
The data coming into my helper function is as follows:
CurrentProd:
{
"id": 28212,
"name": "Camo Onesie",
"slogan": "Blend in to your crowd",
"description": "The So Fatigues will wake you up and fit you in. This high energy camo will have you blending in to even the wildest surroundings.",
"category": "Jackets",
"default_price": "140.00",
"created_at": "2021-07-10T17:00:03.509Z",
"updated_at": "2021-07-10T17:00:03.509Z",
"features": [{
"feature": "Fabric",
"value": "Canvas"
}, {
"feature": "Buttons",
"value": "Brass"
}]
}
ClickedProd:
{
"name": "Morning Joggers",
"category": "Pants",
"originalPrice": "40.00",
"salePrice": null,
"photo": "https://images.unsplash.com/photo-1552902865-b72c031ac5ea?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=300&q=80",
"id": 28214,
"features": [{
"feature": "Fabric",
"value": "100% Cotton"
}, {
"feature": "Cut",
"value": "Skinny"
}]
}
There seems to be a bigger question of how to structure your data. You say that ideally your results would look like:
[
{
"Fabric":
["100% Cotton",28214],
["Canvas",28212]
},
{
"Cut":
["Skinny",28214]
},
{
"Buttons":
["Brass",28212]
}
]
But what you're really trying to get out of this is a combined list of rows and associated values for each item feature, if it exists. All you really need then is an array of keys for each row you want to display, and objects that let you access the needed property by that key.
The array of keys could look like this:
["Fabric", "Cut", "Buttons"]
The objects you want to access the properties using those keys, for example your CurrentProd, could be this (notice that you can access a feature by calling CurrentProd.features["FeatureName"]):
{
"id":28212,
"name":"Camo Onesie",
// ... //
"features": {
"Fabric": "Canvas",
"Buttons": "Brass"
}
}
Having said that, to get those things you can get the array of keys, which we'll call allFeatureKeys, by reducing over a combined array of CurrentProd.features and ClickedProd.features:
const allFeatureKeys = [
...CurrentProd.features,
...ClickedProd.features
].reduce((acc, cur) => {
return acc.findIndex(cur.feature) > -1 ? [...acc, cur.feature] : acc
},
[]
);
And you can modify your CurrentProd to the above data shape by reducing over the array of its features, let's call this modifiedCurrentProd:
const modifiedCurrentProd = {
...CurrentProd,
features: CurrentProd.features.reduce((acc, cur) => {
return {...acc, [cur.feature]: cur.value}
}, {})
}
Repeat that for a modifiedClickedProd object, then you have both CurrentProd.features and ClickedProd.features values available for a lookup when you create your table values.
As an example only, since I don't know your react structure or what data you want to display, you can then render the values in the table rows mapping over the keys to make each row, and for each feature key, you access the value from the modifiedCurrentProd or modifiedClickedProd object's features property:
<div id="table">
{allFeatureKeys.map((featureKey) => {
return <div id="table-row">
<div>{featureKey}</div>
<div>
{
modifiedCurrentProd.features[featureKey] !== undefined
? modifiedCurrentProd.id
: "n/a"
}
</div>
<div>
{
modifiedClickedProd.features[featureKey] !== undefined
? modifiedClickedProd.id
: "n/a"
}
</div>
</div>
})}
</div>
Firstly the target data structure needs to be fixed/optimized. It looks like the OP does concentrate on something which is based on a generic Feature (like Fabric, Cut, Buttons) whereas such feature values seem to be associated more with the Product. Thus for one and the same feature the values are unique to the product feature. In order to not loose the product information, a target format's feature item needs to reflect its related product's id property.
A viable and still flexible enough target data structure then might look like this ...
{
"Fabric": [{
productId: 28214,
value: "100% Cotton",
}, {
productId: 28212,
value: "Canvas",
}],
"Cut": [{
productId: 28214,
value: "Skinny",
}],
"Buttons": [{
productId: 28212,
value: "Brass",
}],
}
Any approach should start with a data-normalizing mapping-process of a product's features list where each feature item will get its product related id assigned.
Thus a feature item like { feature: "Buttons", value: "Brass" } gets mapped temporarily into { productId: 28212, feature: "Buttons", value: "Brass" }.
The two normalized data-item lists now can be concatenated and finally processed/reduced into the final target structure ...
function mergeBoundProductId(item) {
return { ...this, ...item };
}
function aggregateProductFeatureValueLists(index, productFeature) {
const { feature, ...featureValue } = productFeature;
const featureList = index[feature] ??= [];
//const featureList = index[feature] || (index[feature] = []);
featureList.push(featureValue);
return index;
}
function createIndexOfProductFeatureValues(clickedProd, currentProd) {
const { features:clickedFeatures } = clickedProd;
const { features:currentFeatures } = currentProd;
return [
...clickedFeatures.map(mergeBoundProductId, { productId: clickedProd.id }),
...currentFeatures.map(mergeBoundProductId, { productId: currentProd.id }),
].reduce(aggregateProductFeatureValueLists, {});
}
const currentProduct = {
id: 28212,
name: "Camo Onesie",
// ... more properties ...
features: [{
feature: "Fabric",
value: "Canvas",
}, {
feature: "Buttons",
value: "Brass",
}],
};
const clickedProduct = {
name: "Morning Joggers",
// ... more properties ...
id: 28214,
features: [{
feature: "Fabric",
value: "100% Cotton",
}, {
feature: "Cut",
value: "Skinny",
}],
};
console.log(
'createIndexOfProductFeatureValues(clickedProduct, currentProduct) ...',
createIndexOfProductFeatureValues(clickedProduct, currentProduct)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
The advantage of breaking the code into dedicated processes comes with easier refactoring for e.g. changed target structures like something closer to what the OP was looking for.
The changes to the reducer function are minimal. It's just two changes, each barely noticeable in its line ...
function mergeBoundProductId(item) {
return { ...this, ...item };
}
function aggregateProductFeatureValueLists(index, productFeature) {
const { feature, productId, value } = productFeature;
const featureList = index[feature] ??= [];
featureList.push([value, productId]);
return index;
}
function createIndexOfProductFeatureValues(clickedProd, currentProd) {
const { features:clickedFeatures } = clickedProd;
const { features:currentFeatures } = currentProd;
return [
...clickedFeatures.map(mergeBoundProductId, { productId: clickedProd.id }),
...currentFeatures.map(mergeBoundProductId, { productId: currentProd.id }),
].reduce(aggregateProductFeatureValueLists, {});
}
console.log(
'createIndexOfProductFeatureValues(clickedProduct, currentProduct) ...',
createIndexOfProductFeatureValues(clickedProduct, currentProduct)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
const currentProduct = {
id: 28212,
name: "Camo Onesie",
// ... more properties ...
features: [{
feature: "Fabric",
value: "Canvas",
}, {
feature: "Buttons",
value: "Brass",
}],
};
const clickedProduct = {
name: "Morning Joggers",
// ... more properties ...
id: 28214,
features: [{
feature: "Fabric",
value: "100% Cotton",
}, {
feature: "Cut",
value: "Skinny",
}],
};
</script>
The last example's purpose too is to prove the advantage of an easy to refactor code base.
Here the main function gets renamed from createIndexOfProductFeatureValues to createListOfProductFeatureValues.
It's implementation also changes likewise but only in the way how the reducer function gets invoked with its initial value.
The reducer function also does not change dramatically, only in the way of how the accumulating/aggregating collector object gets handled.
And the result is a clean array based object structure ...
function mergeBoundProductId(item) {
return { ...this, ...item };
}
function aggregateProductFeatureValueLists(collector, productFeature) {
const { feature, productId, value } = productFeature;
const { index, list } = collector;
const featureItem = index[feature] ??= { feature, values: [] };
if (featureItem.values.length === 0) {
list.push(featureItem);
}
featureItem.values.push([value, productId]);
return collector;
}
function createListOfProductFeatureValues(clickedProd, currentProd) {
const { features:clickedFeatures } = clickedProd;
const { features:currentFeatures } = currentProd;
return [
...clickedFeatures.map(mergeBoundProductId, { productId: clickedProd.id }),
...currentFeatures.map(mergeBoundProductId, { productId: currentProd.id }),
].reduce(aggregateProductFeatureValueLists, { index: {}, list: [] }).list;
}
console.log(
'createListOfProductFeatureValues(clickedProduct, currentProduct) ...',
createListOfProductFeatureValues(clickedProduct, currentProduct)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
const currentProduct = {
id: 28212,
name: "Camo Onesie",
// ... more properties ...
features: [{
feature: "Fabric",
value: "Canvas",
}, {
feature: "Buttons",
value: "Brass",
}],
};
const clickedProduct = {
name: "Morning Joggers",
// ... more properties ...
id: 28214,
features: [{
feature: "Fabric",
value: "100% Cotton",
}, {
feature: "Cut",
value: "Skinny",
}],
};
</script>
You are already looping through both once. You can get it without reducing.
const formatFeatures = (currentProd, clickedProd) => {
const formattedFeatures = {};
if (clickedProd.features) {
clickedProd.features.forEach(feature => {
const vals = Object.values(feature);
if (!formattedFeatures.hasOwnProperty(vals[0])) {
formattedFeatures[vals[0]] = [];
}
formattedFeatures[vals[0]].push([vals[1], clickedProd.id]);
});
}
currentProd.features.forEach(feature => {
const vals = Object.values(feature);
if (!formattedFeatures.hasOwnProperty(vals[0])) {
formattedFeatures[vals[0]] = [];
}
formattedFeatures[vals[0]].push([vals[1], currentProd.id]);
})
return formattedFeatures;
}
const currentProd = {
"id": 28212,
"name": "Camo Onesie",
"slogan": "Blend in to your crowd",
"description": "The So Fatigues will wake you up and fit you in. This high energy camo will have you blending in to even the wildest surroundings.",
"category": "Jackets",
"default_price": "140.00",
"created_at": "2021-07-10T17:00:03.509Z",
"updated_at": "2021-07-10T17:00:03.509Z",
"features": [{
"feature": "Fabric",
"value": "Canvas"
}, {
"feature": "Buttons",
"value": "Brass"
}]
};
const clickedProd = {
"name": "Morning Joggers",
"category": "Pants",
"originalPrice": "40.00",
"salePrice": null,
"photo": "https://images.unsplash.com/photo-1552902865-b72c031ac5ea?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=300&q=80",
"id": 28214,
"features": [{
"feature": "Fabric",
"value": "100% Cotton"
}, {
"feature": "Cut",
"value": "Skinny"
}]
};
console.log(formatFeatures(currentProd, clickedProd));
.as-console-wrapper { min-height: 100%!important; top: 0; }

How to add or remove children under parent array using react state?

I am working on a solution where I have a deep array of parent having child elements
Here is how the array look like
[
{
"id": "1",
"Name": "John Doe",
"children":
[
{
"id": "1.1",
"name": "John doe 1.1"
},
{
"id:": "1.2",
"name:": "John doe 1.2"
},
{
"id": "1.3",
"name": "John doe 1.3",
"children":
[
{
"id": "1.3.1",
"name": "John doe 1.3.1"
}
]
}
]
},
{
"id": "2",
"Name": "Apple",
"children":
[
{
"id": "2.1",
"name": "Apple 2.1"
},
{
"id:": "1.2",
"name:": "Apple 1.2"
}
]
}
]
basically, I have a functionality where I have a table whenever the user clicks on a row I want to add children related to that row,
For example, whenever I click on the row with id 1, I call click function by passing row as an argument, find an index for row and append children under that along with maintaining state, my solution works only for one level nested child, suppose if I want to add children property under children it's not working
Here is the function that I wrote
const expandRow = (row) => {
const index = _(this.state.data)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.findIndex({ id: row.id });
console.log(index)
if (index !== -1) {
let prevState = [...this.state.data];
let el = _(prevState)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.find({ id: row.id });
console.log(el)
el.children = [
{ id: '_' + Math.random().toString(36).substr(2, 5), name: "sfsdfds1", isExpanded:false,parentId:row.id },
{ id: '_' + Math.random().toString(36).substr(2, 5), name: "sfsdfds2",isExpanded:false,parentId:row.id },
];
this.setState({data:[...this.state.data],prevState},()=>{console.log(this.state.data)})
}
updateState(row.id, { isExpanded: true });
};
I also want to maintain state along with it so whenever the user adds a new row my component re-render.
You need recursive function for this.below is the code I write in VueJs for parent child deep array. please take a look hope it's provide you some idea.
and one more thing my data structure is same as your.
let treeData= {
id:1,
type: 0,
status: 0,
parent_id:0,
children: [{
id:1,
type: 0,
status: 0,
parent_id:1,
children:[
{
id:1,
type: 0,
status: 0,
parent_id:1,
}
]
}],
}
ChangeCheckStatus(treedata, item, status) {
for (let i = 0; i < treedata.length; i++) {
if (treedata[i].id === item.id) {
treedata[i].selectAll = status;
return;
}
this.ChangeCheckStatus(treedata[i].children, item, status);
}
}
makeTreeViewThroughCsvData(csvData) {
const data = this.csvToJSON(csvData)
this.rows_new = [...this.rows_new, ...data];
this.rows_new.forEach((_data) => {
let newNode = {}
for (const key in _data) {
if (_data.hasOwnProperty(key)) {
newNode[key.trim()] = _data[key]
}
}
newNode['children'] = []
newNode['status'] = _data.status
/* eslint-disable */
newNode = rest
//variable hold new tree data
this.treeData.push(newNode)
})
this.generateFinalTreeData();
},
generateFinalTreeData() {
const root = []
const nodeIds = []
const mapping = {}
this.treeData.forEach(node => {
// No parentId means Node
if (node.parent_id != undefined) {
//increment NODE ID only when parent_is is not undefined
nodeIds.push(node.id)
}
if (node.parent_id == 0 || node.parent_id == 1) return root.push(node);
// Insert node as child of parent
let parentKey = mapping[node.parent_id];
if (typeof parentKey !== "number") {
parentKey = this.treeData.findIndex(el => el.id === node.parent_id);
mapping[node.parent_id] = parentKey;
}
if (!this.treeData[parentKey].children) {
return this.treeData[parentKey].children = [node];
}
this.treeData[parentKey].children.push(node);
});
this.finalTreeData = root
//vuex commit statement == Redux dispach
this.$store.commit('setTreeViewData', root);
this.$store.commit('setMaxNodeId', Math.max(...nodeIds) + 1);
}

Map Json data by JavaScript

I have a Json data that I want to have in a different format.
My original json data is:
{
"info": {
"file1": {
"book1": {
"lines": {
"102:0": [
"102:0"
],
"105:4": [
"106:4"
],
"106:4": [
"107:1",
"108:1"
]
}
}
}
}
}
And I want to map it as following:
{
"name": "main",
"children": [
{
"name": "file1",
"children": [
{
"name": "book1",
"group": "1",
"lines": [
"102",
"102"
],
[
"105",
"106"
],
[
"106",
"107",
"108"
]
}
],
"group": 1,
}
],
"group": 0
}
But the number of books and number of files will be more. Here in the lines the 1st part (before the :) inside the "" is taken ("106:4" becomes "106"). The number from the key goes 1st and then the number(s) from the value goes and make a list (["106", "107", "108"]). The group information is new and it depends on parent-child information. 1st parent is group 0 and so on. The first name ("main") is also user defined.
I tried the following code so far:
function build(data) {
return Object.entries(data).reduce((r, [key, value], idx) => {
//const obj = {}
const obj = {
name: 'main',
children: [],
group: 0,
lines: []
}
if (key !== 'reduced control flow') {
obj.name = key;
obj.children = build(value)
if(!(key.includes(":")))
obj.group = idx + 1;
} else {
if (!obj.lines) obj.lines = [];
Object.entries(value).forEach(([k, v]) => {
obj.lines.push([k, ...v].map(e => e.split(':').shift()))
})
}
r.push(obj)
return r;
}, [])
}
const result = build(data);
console.log(result);
The group information is not generating correctly. I am trying to figure out that how to get the correct group information. I would really appreciate if you can help me to figure it out.
You could use reduce method and create recursive function to build the nested structure.
const data = {"info":{"file1":{"book1":{"lines":{"102:0":["102:0"],"105:4":["106:4"],"106:4":["107:1","108:1"]}}}}}
function build(data) {
return Object.entries(data).reduce((r, [key, value]) => {
const obj = {}
if (key !== 'lines') {
obj.name = key;
obj.children = build(value)
} else {
if (!obj.lines) obj.lines = [];
Object.entries(value).forEach(([k, v]) => {
obj.lines.push([k, ...v].map(e => e.split(':').shift()))
})
}
r.push(obj)
return r;
}, [])
}
const result = build(data);
console.log(result);
I couldn't understand the logic behind group property, so you might need to add more info for that, but for the rest, you can try these 2 functions that recursively transform the object into what you are trying to get.
var a = {"info":{"file1":{"book1":{"lines":{"102:0":["102:0"],"105:4":["106:4"],"106:4":["107:1","108:1"]}}}}};
var transform = function (o) {
return Object.keys(o)
.map((k) => {
return {"name": k, "children": (k === "lines" ? parseLines(o[k]) : transform(o[k])) }
}
)
}
var parseLines = function (lines) {
return Object.keys(lines)
.map(v => [v.split(':')[0], ...(lines[v].map(l => l.split(":")[0]))])
}
console.log(JSON.stringify(transform(a)[0], null, 2));

transform array of strings into custom structure

I need to convert this array of strings into a special structure
https://github.com/jonmiles/bootstrap-treeview
This is my Input String array:
******************
"productone"
"productone\level2\level3"
"productwo"
"productwo\level2\level3\level4"
"productwo\level2\level3.1\level4\level5"
"productwo\level2\level3.2\level4\level5"
so can you imagine this:
"memory"
"memory\ram"
"memory\ram\ddr\sodimm\533mhz\4gb"
"memory\ram\ddr\sodimm\533mhz\8gb"
"memory\ram\ddr\sodimm\533mhz\16gb"
"memory\ram\ddr\sodimm2\633mh\4gb
"memory\ram\ddr\sodimm2\633mh\16gb
"memory\disk"
and so on....
*******************
And I need this Output (pay attention at return correct order output):
var jsondata = [
{
"text": "productone",
"nodes":[ {"text": "level2",
"nodes":[{"text": "level3"}]
}]
},
{
"text": "productwo",
"nodes":[{"text": "level2"},
"nodes":[{"text": "level3",
"nodes":[{text:level4}]
}]
}]
}
}]
Any suggestion?
I threw away my previous answer to replace it with this one.
This should do exactly what you want.
var src = [
"productone",
"productone\\level2\\level3",
"productwo\\level2\\level3\\level4",
"productone\\level2\\dog",
"productone\\level2\\dog\\bark",
"productwo\\level2\\level3a\\level4a",
"productwo\\level2\\level3\\level4\\level5",
"productwo\\food\\desserts\\cookies",
"productwo\\food\\desserts\\cakes",
"productwo\\food\\desserts\\pies",
"productone\\level2\\cat",
"productone\\level2\\cat\\meow"
]
function tempToObj(temp) {
var result = [];
Object.keys(temp).forEach(
function(key) {
var obj = {
text: key
};
var nodes = tempToObj(temp[key]);
if (nodes.length > 0) {
obj.nodes = nodes;
}
result.push(obj);
}
);
return result;
}
function strsToObj(strList) {
var result = [];
var tempResult = {};
function buildNode(parts, idx, obj) {
var key = parts[idx];
obj[key] = obj[key] || {};
idx++;
if (idx < parts.length) {
buildNode(parts, idx, obj[key]);
}
}
strList.forEach(
function(str) {
var parts = str.split('\\');
buildNode(parts, 0, tempResult);
}
);
return tempToObj(tempResult);
}
var obj = strsToObj(src);
console.log(JSON.stringify(obj,0,2));
I found it much easier to just build of an object structure and then convert it into the format you wanted. That simplified the parsing algorithm and yet prevents rebuilding everything each time.
The result of the code above is this:
[
{
"text": "productone",
"nodes": [
{
"text": "level2",
"nodes": [
{
"text": "level3"
},
{
"text": "dog",
"nodes": [
{
"text": "bark"
}
]
},
{
"text": "cat",
"nodes": [
{
"text": "meow"
}
]
}
]
}
]
},
{
"text": "productwo",
"nodes": [
{
"text": "level2",
"nodes": [
{
"text": "level3",
"nodes": [
{
"text": "level4",
"nodes": [
{
"text": "level5"
}
]
}
]
},
{
"text": "level3a",
"nodes": [
{
"text": "level4a"
}
]
}
]
},
{
"text": "food",
"nodes": [
{
"text": "desserts",
"nodes": [
{
"text": "cookies"
},
{
"text": "cakes"
},
{
"text": "pies"
}
]
}
]
}
]
}
]
I have solve my problem and I have transform to Javacript pure code this
Brandon Clapp article
You can string#split each string inside array#map and use array#reduceRight to check for nodes and text value inside your result object. If text key is present at a level, reassign your object to nodes key and populate text key with the current text value.
var strings = ["productone","productone\\level2\\level3","productwo\\level2\\level3\\level4"];
const result = strings.map(string => string.split('\\').reduceRight((r,text, index) => {
if(r['text'])
r['nodes'] = [Object.assign({}, r)];
r['text'] = text;
return r;
},{}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could take a hash table with a nested approach by taking the given part strings as identifiert for nested objects for collecting all data.
Later this proposal deletes unwanted nodes without content.
This approach works for unsorted data.
var array = ["productone", "productone\\level2\\level3", "productwo\\level2\\level3\\level4"],
result = [],
hash = { _: result };
array.forEach(function (a) {
a.split('\\').reduce(function (r, k) {
if (!r[k]) {
r[k] = { _: [] };
r._.push({ text: k, nodes: r[k]._ });
}
return r[k];
}, hash);
});
result.forEach(function clean(o) {
if (o.nodes.length) {
o.nodes.forEach(clean);
} else {
delete o.nodes;
}
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories