Construir JSON a partir de un array de JavaScript de forma recursiva
Quiero construir un objeto JSON a partir de un array devuelto por una solicitud HTTP, mi array actual se ve así:
[{
Description: "Product"
IsInstanciable: false
IsMasked: false
Name: "ProductBase"
Subclasses: [{
Description: "Product2"
IsInstanciable: false
IsMasked: false
Name: "ProductBase2",
Count: 5,
Subclasses:[]
},
{
Description: "Product3"
IsInstanciable: false
IsMasked: false
Name: "ProductBase3",
Count: 4,
Subclasses:[],
}]
},
{
Description: "Product4"
IsInstanciable: false
IsMasked: false
Name: "ProductBase4",
Count: '...',
Subclasses: [...]
}
Quiero crear un objeto JSON de manera recursiva a partir del arreglo anterior. Se verá así:
[
{
name: 'Product',
Count: 9,
children: [
{name: 'Product2'},
{name: 'Product3'},
]
}, {
name: 'Product4',
children: [
{
name: '...',
Count: 'Sum of Count in all children by level'
children: [
{name: '...'},
{name: '...'},
]
}
]
},
];
Aquí está mi función recursiva en TypeScript, pero no funciona como esperaba. ¿Cómo puedo arreglarla?
recursive(data, stack: TreeNode[]) {
let elt: TreeNode = {name:'', children: []}
if(data.Subclasses.length > 0) {
elt.name = data.Description;
data.Subclasses.forEach(element => {
elt.children.push({name: element.Description});
this.recursive(element, stack);
});
}else {
elt.name = data.Description;
}
stack.push(elt);
}
Respuesta #1
Puedes usar la función map junto con la función recursiva de esta manera:
function MapObject(object) {
if(!object || object.length < 0) return [];
return object.map(obj => { return {
name: obj.Description,
children: MapObject(obj.Subclasses)
}});
}
Sigue un ejemplo completo de trabajo:
var example = [{
Description: "Product",
IsInstanciable: false,
IsMasked: false,
Name: "ProductBase",
Subclasses: [{
Description: "Product2",
IsInstanciable: false,
IsMasked: false,
Name: "ProductBase2",
Subclasses:[]
},
{
Description: "Product3",
IsInstanciable: false,
IsMasked: false,
Name: "ProductBase3",
Subclasses:[]
}]
},
{
Description: "Product4",
IsInstanciable: false,
IsMasked: false,
Name: "ProductBase4"
}]
function MapObject(object) {
if(!object || object.length < 0) return [];
return object.map(obj => { return {
name: obj.Description,
children: MapObject(obj.Subclasses)
}});
}
console.log(MapObject(example));Respuesta #2
Dividiría esto en algunas funciones separadas con responsabilidades claras:
const sum = (ns) =>
ns .reduce ((a, b) => a + Number (b), 0)
const getCount = (product) =>
(product .Count || 0) + sum ((product .Subclasses || []) .map (getCount))
const extract = (product) =>
({
name: product .Name,
count: getCount (product),
children: product .Subclasses .map (extract)
})
const extractAll = (products) =>
products .map (extract)
const products = [{"Description": "Product", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase", "Subclasses": [{"Count": 5, "Description": "Product2", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase2", "Subclasses": []}, {"Count": 4, "Description": "Product3", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase3", "Subclasses": []}]}, {"Count": 3, "Description": "Product4", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase4", "Subclasses": []}]
console .log (
extractAll (products)
)(Tenga en cuenta que cambié el caso de la propiedad count de la salida. Simplemente me pareció más consistente con name y children.)
Recurrimos a través del objeto para obtener su propiedad Count, y recurrimos por separado para obtener el resto del objeto. Aunque podría haber una manera de combinar esto, esperaría que el código fuera significativamente más complejo.
Tenga en cuenta que asumí que todas las propiedades Count son en realidad numéricas, aunque su ejemplo anterior incluya una cadena. Si tiene valores como "3", entonces getCount tendrá que ser un poco más sofisticado.
