Convert flat array to nested parent-child format - javascript

Currently, I have an array with below format:
[{
key: "a"
}, {
key: "b"
}, {
key: "c"
}, {
key: "d"
}, {
key: "e"
}]
Every element in the array is the parent of element next to it.
Required to convert it to below format:
[{
key: "a",
Nodes: [{
key: "b",
Nodes: [{
key: "c",
Nodes: [{
key: "d",
Nodes: [{
key: "e"
}]
}]
}]
}]
}]
I have achieved this, but the logic I have implemented is quite lengthy and now I want to optimize code.
So I want to know the most optimized way to do this

Using Array#reduceRight makes this simple:
const array = [{
key: "a"
}, {
key: "b"
}, {
key: "c"
}, {
key: "d"
}, {
key: "e"
}];
const nested = array.reduceRight((Nodes, obj) => {
if (Nodes) {
return [Object.assign({}, obj, { Nodes })];
} else {
return [obj];
}
}, null);
console.log(nested);

Related

Transforming array of objects based upon key - javascript

I am trying to figure out the most efficient method for reducing an array of objects based upon a unique key (key/values are dynamically returned in this case). I've tried to a combination of different methods using concat, map, or filter but haven't had much luck.
Original array of objects:
[
{
key: "Name",
value: "John"
},
{
key: "Company",
value: "Acme"
},
{
key: "Name",
value: "Jack"
},
{
key: "Name",
value: "Matt"
},
{
key: "Last",
value: "Test"
}
]
Desired Array:
[
{
key: "Name",
values: [
"John",
"Jack",
"Matt"
]
},
{
key: "Company",
values: [
"Acme"
]
},
{
key: "Last",
values: [
"Test"
]
}
]
Prob other ways, but a simple for loop would suffice:
const data = [{
key: "Name",
value: "John"
},
{
key: "Company",
value: "Acme"
},
{
key: "Name",
value: "Jack"
},
{
key: "Name",
value: "Matt"
},
{
key: "Last",
value: "Test"
}
]
let result = {}
for (const i in data) {
result[data[i].key] = {
key: data[i].key,
values: [
...result[data[i].key] ? result[data[i].key].values : [],
data[i].value
]
}
}
console.log(Object.values(result))
You can use reduce to build a new object using name as the keys, and then use Object.values to create the output you need from the object:
const data = [
{ key: "Name", value: "John" },
{ key: "Company", value: "Acme" },
{ key: "Name", value: "Jack" },
{ key: "Name", value: "Matt" },
{ key: "Last", value: "Test" }
];
const out = Object.values(data.reduce((acc, { key, value }) => {
// If the key doesn't exist on the object, add it
// and initialise the value object
acc[key] = acc[key] || { key, values: [] };
// Push the value from the current iteration
// into the values array
acc[key].values.push(value);
// Return the accumulator for the next iteration
return acc;
}, {}));
console.log(out);
I think reduce is the best solution here :)
const initialArray = [
{
key: "Name",
value: "John"
},
{
key: "Company",
value: "Acme"
},
{
key: "Name",
value: "Jack"
},
{
key: "Name",
value: "Matt"
},
{
key: "Last",
value: "Test"
}
];
const result = initialArray.reduce((acc, obj) => {
/* try to find object in the result array
returns index or -1 if object is missing
*/
const existingIndex = acc.findIndex(item => item.key === obj.key);
if (existingIndex > -1) {
/* object already exists, update its values array */
acc[existingIndex].values.push(obj.value);
return acc;
} else {
/* the key is first encountered, create an object in the result array */
acc.push({
key: obj.key,
values: [obj.value],
});
return acc;
}
}, []); // [] - default value is an empty array
console.log(result);

How to create nested child objects in JavaScript from array?

This is the given array:
[{
key: 1,
nodes: {}
}, {
key: 2,
nodes: {}
}, {
key: 3,
nodes: {}
}]
How to create nested child objects in JavaScript from this array?
[{
key: 1,
nodes: [{
key: 2,
nodes: [{
key: 3,
nodes: []
}]
}]
}];
This is a pretty good use case for reduceRight which allows you to build the structure from the inside out:
let arr = [{
key: 1,
nodes: {}
}, {
key: 2,
nodes: {}
}, {
key: 3,
nodes: {}
}]
let a = arr.reduceRight((arr, {key}) => [{key, nodes: arr}],[])
console.log(a)
It's working fine. Try this below code
const firstArray = [{ key: 1, nodes: {} }, { key: 2, nodes: {} }, { key: 3, nodes: {} }];
firstArray.reverse();
const nestedObject = firstArray.reduce((prev, current) => {
return {
...current,
nodes:[{...prev}]
}
}, {});
console.log(nestedObject)

Mapping multidimensional array of objects in JavaScript

I need to recreate a multidimensional array of objects only by looking at a single node.
I tried using recursive function in a loop(Array.map).
obj = [{
key: 0,
children: [{
key: 1,
children: [{
key: 2,
children: [{
key: 3,
children: []
},
{
key: 4,
children: []
}]
},
{
key: 5,
children: [{
key: 6,
children: []
},
{
key: 7,
children: []
},
{
key: 8,
children: []
}]
}]
}]
}]
function test(arg, level=0, arry=[]){
arg.map((data, i) => {
if(!data.arry){
arry.push(data.key);
data.arry = arry;
}
if(data.children){
test(data.children, level+1, data.arry);
}
})
}
test(obj);
Function test should build and return exactly the same object as obj.
This is only a simplified version of the problem I have and that's why it looks weird(returning an object I already have). My original problem is about fetching parts of n-dimensional array of objects from DB but without knowing its original dimensions. So I have to "discover" the dimensions and then build the exact same copy of the object.
An example implementation would be this: Recursively iterating over array and objects and deep-copying their properties/children. This creates a deep copy of an object, and tests that data is actually copied and not a reference anymore:
obj = [{
key: 0,
children: [{
key: 1,
children: [{
key: 2,
children: [{
key: 3,
children: []
},
{
key: 4,
children: []
}]
},
{
key: 5,
children: [{
key: 6,
children: []
},
{
key: 7,
children: []
},
{
key: 8,
children: []
}]
}]
}]
}];
function copy(obj) {
let copiedObject;
if (Array.isArray(obj)) {
// for arrays: deep-copy every child
copiedObject = [];
for (const child of obj) {
copiedObject.push(copy(child));
}
} else if (typeof obj === 'object') {
// for objects: deep-copy every property
copiedObject = {};
for (const key in obj) {
copiedObject[key] = copy(obj[key]);
}
} else {
// for primitives: copy the value
return obj;
}
return copiedObject;
}
function test() {
const cpy = copy(obj);
// make sure that deep-copying worked
console.log('Values from obj should be exactly copied to cpy: ' + (cpy[0].children[0].children[0].key === obj[0].children[0].children[0].key));
// make sure that references are broken, so that when data from the original object is updated, the updates do
// NOT reflect on the copy
obj[0].children[0].children[0].key = Math.random();
console.log('Changes to obj should not reflect on cpy: ' + (cpy[0].children[0].children[0].key !== obj[0].children[0].children[0].key));
}
test();

Javascript: Print all paths in a JSON tree including arrays

Example JSON:
{
name: "A",
childNodes: [{
name: "B",
childProps: [{
prop_list: [{
name: "BS1"
}, {
name: "BS2"
}]
}],
childNodes: [{
name: "C",
childProps: [{
prop_list: [{
name: "CS1"
}, {
name: "CS2"
}]
}],
}]
}],
childProps: [{
prop_list: [{
name: "AS1"
}, {
name: "AS2"
}]
}]
}
I want to print output in the following manner.
A, B, BS1
A, B, BS2
A, B, C, CS1
A, B, C, CS2
A, AS1
A, AS2
Basically, I want to print the path to each leaf node. (And I need to get the name property value for each object)
To solve your problem you must loop over each item in your object and use a recursive function to append nested names to your path string.
Here is an example to get you started
var obj = {
name: "A",
childNodes: [{
name: "B",
childProps: [{
prop_list: [{
name: "BS1"
}, {
name: "BS2"
}]
}],
childNodes: [{
name: "C",
childProps: [{
prop_list: [{
name: "CS1"
}, {
name: "CS2"
}]
}],
}]
}],
childProps: [{
prop_list: [{
name: "AS1"
}, {
name: "AS2"
}]
}]
};
(function findName(o, path = '') {
const isLastLevel = !o.childProps && !o.childNodes;
path += isLastLevel ? o.name : o.name + ', ';
if (o.childProps) {
o.childProps.forEach(props => {
props.prop_list.forEach(prop => {
findName(prop, path);
});
});
}
if (o.childNodes) {
o.childNodes.forEach(node => {
findName(node, path);
});
}
if (isLastLevel) {
console.log(path);
}
})(obj);
Perhaps you could take a more generalized approach to implementing this using recusion, so that your traversal works for any format of input data:
function printPaths(node, path) {
const nodeName = node.name ? (path ? ',' : '') + node.name : ''
const nodePath = (path ? path : '') + nodeName
const nodeChildren = Object.values(node).filter(Array.isArray).flat()
return nodeChildren.length > 0 ? nodeChildren
.map(nodeChild => printPaths(nodeChild, nodePath)).flat() : nodePath
}
This solution is decoupled from your specific format of input data, meaning it will examine the current node object and traverse any array values found, eventually returning all discovered the paths:
var input = {
name: "A",
childNodes: [{
name: "B",
childProps: [{
prop_list: [{
name: "BS1"
}, {
name: "BS2"
}]
}],
childNodes: [{
name: "C",
childProps: [{
prop_list: [{
name: "CS1"
}, {
name: "CS2"
}]
}],
}]
}],
childProps: [{
prop_list: [{
name: "AS1"
}, {
name: "AS2"
}]
}]
}
function printPaths(node, path) {
const nodeName = node.name ? (path ? ',' : '') + node.name : ''
const nodePath = (path ? path : '') + nodeName
const nodeChildren = Object.values(node).filter(Array.isArray).flat()
return nodeChildren.length > 0 ? nodeChildren
.map(nodeChild => printPaths(nodeChild, nodePath)).flat() : nodePath
}
console.log( printPaths(input) )

how to convert array of object into object?

I have an array of object and i cant convert it into an object. The array of object that i have is as below:
var x = [{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
},
{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}];
and i want to convert it into object like this:
{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
},{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}
I used one of the stackoverflow solution
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
rv[i] = arr[i];
return rv;
}
But it convert as follows:
{
"0":{
"name":"a",
"items":[{"name":"b","items":[{"name":"c"}]}]},
"1":{
"name":"d",
"items":[{"name":"e","items":[{"name":"f"}]}]}
}
So, how to get the object in the format i gave?
The following is not one object
{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
}, // this comma shows you that it's can't be one object.
{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}
You can't have an object with the two different properties of the same name.
You want to reduce the array into a single object:
var output = x.reduce(function(output, arrayItem) {
var key = arrayItem.name;
output[key] = arrayItem;
return output;
}, { });
maybe you want something like this
var x = [{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
},
{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}];
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
rv[arr[i].name] = { items: arr[i].items} ;
return rv;
}
console.log(toObject(x));
http://jsfiddle.net/jypahwj4/
One problem that your doing to run into is that when creating an object from that array you will have multiple keys with the same name so each name: will be overridden by the next one you try and add to the object. In your function because you are doing rv[i] and i is a number that is why you are getting the numbers in your newly created object.
A possible solution would be
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
rv[arr[i].name] = arr[i].items;
return rv;
}
But this would make the key of the outer part of the object be equal to the name: of the original array of objects

Categories

Resources