This question already has answers here:
Generify transformation of hierarchical array into a flat array
(2 answers)
Closed 3 years ago.
I have variable rawData like this:
let rawData = [
{
title: '1',
result: '1',
child: [
{
title: '1-1',
result: '1-1',
child: [
{
title: '1-1-1',
result: '1-1-1',
child: [
{
title: '1-1-1-1',
result: '1-1-1-1',
child: [
{
title: '1-1-1-1-1',
result: '1-1-1-1-1',
},
],
},
{
title: '1-1-1-2',
result: '1-1-1-2',
},
{
title: '1-1-1-3',
result: '1-1-1-3',
},
],
},
],
},
],
},
];
and with my function. it runs as expected, here's my function:
let normalizeArray = [];
function test(array) {
for (const key in array) {
if (array.hasOwnProperty(key)) {
const element = array[key];
if (element.hasOwnProperty('child')) {
test(element.child);
delete element.child;
normalizeArray.unshift(Object.assign({}, element));
} else {
normalizeArray.push(Object.assign({}, element));
}
}
}
}
and return (expected):
[
{ title: '1', result: '1' },
{ title: '1-1', result: '1-1' },
{ title: '1-1-1', result: '1-1-1' },
{ title: '1-1-1-1', result: '1-1-1-1' },
{ title: '1-1-1-1-1', result: '1-1-1-1-1' },
{ title: '1-1-1-2', result: '1-1-1-2' },
{ title: '1-1-1-3', result: '1-1-1-3' },
];
But, if the rawData like this:
let rawData = [
{
title: '1',
result: '1',
child: [
{
title: '1-1',
result: '1-1',
child: [
{
title: '1-1-1',
result: '1-1-1',
child: [
{
title: '1-1-1-1',
result: '1-1-1-1',
child: [
{
title: '1-1-1-1-1',
result: '1-1-1-1-1',
},
],
},
{
title: '1-1-1-2',
result: '1-1-1-2',
},
{
title: '1-1-1-3',
result: '1-1-1-3',
},
],
},
],
},
],
},
{
title: '2',
result: '2',
child: [
{
title: '2-2',
result: '2-2',
},
],
},
{
title: '3',
result: '3',
},
];
with my function, it return :
[
{ title: '2', result: '2' },
{ title: '1', result: '1' },
{ title: '1-1', result: '1-1' },
{ title: '1-1-1', result: '1-1-1' },
{ title: '1-1-1-1', result: '1-1-1-1' },
{ title: '1-1-1-1-1', result: '1-1-1-1-1' },
{ title: '1-1-1-2', result: '1-1-1-2' },
{ title: '1-1-1-3', result: '1-1-1-3' },
{ title: '2-2', result: '2-2' },
{ title: '3', result: '3' },
];
How I could got a result like this:
[
{ title: '1', result: '1' },
{ title: '1-1', result: '1-1' },
{ title: '1-1-1', result: '1-1-1' },
{ title: '1-1-1-1', result: '1-1-1-1' },
{ title: '1-1-1-1-1', result: '1-1-1-1-1' },
{ title: '1-1-1-2', result: '1-1-1-2' },
{ title: '1-1-1-3', result: '1-1-1-3' },
{ title: '2', result: '2' },
{ title: '2-2', result: '2-2' },
{ title: '3', result: '3' },
];
And if you could help me to produce leaner code, feel free to help me. I'm still new in javascript
Instead of deleting/shifting/unshifting, you can extract the properties from the array item immediately with destructuring. Push the title and result to the result array as an object, then do a recursive call if child exists. This way, the result array will be the output of an ordinary depth-first strategy:
function doFlat(inputArr, resultsArr = []) {
for (const { child, title, result } of inputArr) {
resultsArr.push({ title, result });
if (child) {
doFlat(child, resultsArr);
}
}
return resultsArr;
}
By putting the results array as a default parameter which gets passed along to every recursive call, you only ever create one array, which is more efficient than creating a new array for every function call and then iterating over it in its caller.
function doFlat(inputArr, resultsArr = []) {
for (const { child, title, result } of inputArr) {
resultsArr.push({ title, result });
if (child) {
doFlat(child, resultsArr);
}
}
return resultsArr;
}
let rawData = [
{
title: '1',
result: '1',
child: [
{
title: '1-1',
result: '1-1',
child: [
{
title: '1-1-1',
result: '1-1-1',
child: [
{
title: '1-1-1-1',
result: '1-1-1-1',
child: [
{
title: '1-1-1-1-1',
result: '1-1-1-1-1',
},
],
},
{
title: '1-1-1-2',
result: '1-1-1-2',
},
{
title: '1-1-1-3',
result: '1-1-1-3',
},
],
},
],
},
],
},
{
title: '2',
result: '2',
child: [
{
title: '2-2',
result: '2-2',
},
],
},
{
title: '3-3',
result: '3-3',
},
];
console.log(doFlat(rawData));
It could get clean looking with spread operator and recursion
function flatten(dataArray) {
const result = [];
dataArray.map(d => {
const { child, ...rest } = d;
result.push(rest);
if(child && child.length) result.push(...flatten(child));
});
return result;
}
console.log(flatten(rawData));
Hope it helps
Related
This question already has answers here:
Tree structure to flat array Javascript
(2 answers)
Closed 12 months ago.
I want to get length of multidimentionnal array in JS :
This is my array :
const treeData = [
{
title: '0-0',
key: '0-0',
children: [
{
title: '0-0-0',
key: '0-0-0',
children: [
{
title: '0-0-0-0',
key: '0-0-0-0',
},
{
title: '0-0-0-1',
key: '0-0-0-1',
},
{
title: '0-0-0-2',
key: '0-0-0-2',
},
],
},
{
title: '0-0-1',
key: '0-0-1',
children: [
{
title: '0-0-1-0',
key: '0-0-1-0',
},
{
title: '0-0-1-1',
key: '0-0-1-1',
},
{
title: '0-0-1-2',
key: '0-0-1-2',
},
],
},
{
title: '0-0-2',
key: '0-0-2',
},
],
},
{
title: '0-1',
key: '0-1',
children: [
{
title: '0-1-0-0',
key: '0-1-0-0',
},
{
title: '0-1-0-1',
key: '0-1-0-1',
},
{
title: '0-1-0-2',
key: '0-1-0-2',
},
],
},
{
title: '0-2',
key: '0-2',
},
];
The output should be : 15
The idea is to map trough all elements and if they have an array child, get the lentgh of it
I'm sure that I will go for a récursive function but it seems to be tricky..
I did'nt found any solutions in internet, have you an idea please ?
Thank you
function getLengthOfTreeData(treeData) {
let size = { size: 0 }; // object because it needs to be passed by reference
return getSize(size, treeData).size;
}
function getSize(size, treeData) { // recursive function
if (treeData.length === 0) {
return size;
}
size.size += treeData.length;
for (let i = 0; i < treeData.length; i++) {
const data = treeData[i];
if (data.children) getSize(size, data.children);
}
return size;
}
console.log(getLengthOfTreeData(treeData));
I have an array form lke this
const options = [
{
id: 1,
content: 'Hello'
},
{
id: 2,
content: 'Hi'
}
]
const answers = [
{
id: 400,
content: 'World',
optionKey: 1
},
{
id: 500,
content: 'There',
optionKey: 2
}
]
expected output
const expecetedOutput = [
{
option: {
id: 1,
content: 'Hello'
},
answer: {
id: 400,
content: 'World'
}
},
{
option: {
id: 2,
content: 'Hi'
},
answer: {
id: 500,
content: 'There'
}
}
];
What i have tried
i have tried using map and find but the result still not as expected
const optionWithAnswer = answers.map((answer) => {
return {
option: options.find((option) => option.id === answer.optionKey),
answer: answers.find((answer) => answer.optionKey === options.find((option) => option.id === answer.optionKey).id)
}
})
result i got. the answer will always found the first index of answer array
[
{
option: { id: 1, content: 'Hello' },
answer: { id: 400, content: 'World', optionKey: 1 }
},
{
option: { id: 2, content: 'Hi' },
answer: { id: 400, content: 'World', optionKey: 1 }
}
]
what i'm supposed to do. is this something that should accomplish using reduce ?
options.map(opt => ({
option: opt,
answer: answers.find(i => i.optionKey === opt.id)
}));
Example of the data (useContext)
data: {
projects:{
'project-1':{
name: 'project-1',
columnIds:['column-1', 'column-2'],
},
'project-2':{
name: 'project-2',
columnIds:['column-3'],
},
},
columns:{
'column-1':{
title: 'column-1',
content: 'abc',
},
'column-2':{
title: 'column-2',
content: 'def',
},
'column-3':{
title: 'column-3',
content: 'ghi',
},
},
}
If I delete the project-1 which has column-1 and column-2, it should also be deleted inside the columns object. So the result should be:
data: {
projects:{
'project-2':{
name: 'project-2',
columnIds:['column-3'],
},
},
columns:{
'column-3':{
title: 'column-3',
content: 'ghi',
},
},
}
I already know how to remove project-1 object but I'm stuck in removing the column-1 and column-2.
This was my code for removing column-1 and `column-2 data:
let newColumn = data.projects[projectname].columnIds.map((item) =>
Object.keys(data.columns).filter((key) => key !== item)
);
You just need to check if there are corresponding column IDs before you delete the property. If there are, iterate over the IDs and delete the columns:
const data = {
projects: {
'project-1': {
name: 'project-1',
columnIds: ['column-1', 'column-2'],
},
'project-2': {
name: 'project-2',
columnIds: ['column-3'],
},
},
columns: {
'column-1': {
title: 'column-1',
content: 'abc',
},
'column-2': {
title: 'column-2',
content: 'def',
},
'column-3': {
title: 'column-3',
content: 'ghi',
},
},
}
const key = 'project-1'
const { columnIds } = data.projects[key]
if (columnIds.length > 0) {
columnIds.forEach(id => {
delete data.columns[id]
})
}
delete data.projects[key]
console.log(data)
You can also create a copy of the object so you don't mutate the original one:
const data = {
projects: {
'project-1': {
name: 'project-1',
columnIds: ['column-1', 'column-2'],
},
'project-2': {
name: 'project-2',
columnIds: ['column-3'],
},
},
columns: {
'column-1': {
title: 'column-1',
content: 'abc',
},
'column-2': {
title: 'column-2',
content: 'def',
},
'column-3': {
title: 'column-3',
content: 'ghi',
},
},
}
function deleteColumns(obj, key) {
const copy = {
projects: { ...obj.projects },
columns: { ...obj.columns }
}
const { columnIds } = copy.projects[key]
if (columnIds.length > 0) {
columnIds.forEach(id => delete copy.columns[id])
}
delete copy.projects[key]
return copy
}
const afterDelete = deleteColumns(data, 'project-1')
console.log(afterDelete)
console.log(data)
I have an example array
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
I'm looking to modify the array to:
const modifiedArray = [
{ obj: [{ title: 'titleValue1' }, { title: 'titleValue2' }] },
{ obj: [{ title: 'titleValue3' }, { title: 'titleValue4' }] },
]
So when I loop over the modified array I can call 'obj.title' instead of 'obj.fields.title'
I think this can be achieved using .map. So far I have tried:
const ammendedArray = array.map(item => ({ ...item, title: item.map(e => e.fields) }))
But returning 'item.map is not a function'
Any help with this would be much appreciated.
In your code you are trying to use map for an item in the top level array. Which is like this for the first item,
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] }
As you can see item is an object. You can not map through an object. What you can do is map through item.obj
const ammendedArray = array.map(item => ({ ...item, title: item.obj.map(e => e.fields) }))
But it will not solve your problem you will get a wrong array of objects like this,
[
{
obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }],
title: [{ title: 'titleValue1' }, { title: 'titleValue2' }]
},
...
]
You will have to update the obj key instead. What you need to do is the following,
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
const res = array.map((item) => {
return {
obj: item.obj.map(i => {
return i.fields
})
};
});
console.log(res);
I could reach to that like this :)
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
// pass a function to map
const map1 = array.map((x)=>{
const filedsArray = [...x.obj]
x.obj = []
filedsArray.forEach((y)=>{
x.obj.push({title:y.fields.title})
})
return x
})
console.log(map1);
I would like to convert this json / object to this specific structure below to allow me to use a treeList component.
I've tried to build a recursive function but I didn't find the solution yet.
Thanks for your help
const data = {
parent1: {
child1: { bar: "1" },
child2: "2"
},
parent2: {
child1: "1"
}
}
to
const treeData = [
{
title: "parent1",
key: "parent1",
children: [
{
title: "child1",
key: "child1",
children: [{ title: "bar", key: "bar", value: "1" }]
},
{
title: "child2",
key: "child2",
value: "2"
}
],
},
{
title: "parent2",
key: "parent2",
children: [
{
title: "child1",
key: "child1",
value: "1"
}
]
}
]
You could take an iterative and recursive approach.
function getNodes(object) {
return Object
.entries(object)
.map(([key, value]) => value && typeof value === 'object'
? { title: key, key, children: getNodes(value) }
: { title: key, key, value }
);
}
const data = { parent1: { child1: { bar: "1" }, child2: "2" }, parent2: { child1: "1" } },
result = getNodes(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
just share sample, a little different from yours. But it give you a hint with recursive function.
https://jsfiddle.net/segansoft/7bdxmys4/1/
function getNestedChildren(arr, parent) {
var out = []
for (var i in arr) {
if (arr[i].parent == parent) {
var children = getNestedChildren(arr, arr[i].id)
if (children.length) {
arr[i].children = children
}
out.push(arr[i])
}
}
return out
}
var flat = [{
id: 1,
title: 'hello',
parent: 0
},
{
id: 2,
title: 'hello',
parent: 0
},
{
id: 3,
title: 'hello',
parent: 1
},
{
id: 4,
title: 'hello',
parent: 3
},
{
id: 5,
title: 'hello',
parent: 4
},
{
id: 6,
title: 'hello',
parent: 4
},
{
id: 7,
title: 'hello',
parent: 3
},
{
id: 8,
title: 'hello',
parent: 2
}
]
var nested = getNestedChildren(flat, 0)
document.write('<pre>' + JSON.stringify(nested, 0, 4) + '</pre>');