Vuetify TreeView Data Manipulation - javascript

I am trying to use v-treeview to create a nested list of items. This list of items could be 10 layers deep or 100, each with 100 items within. How would I go about turning a few hundred objects into one large object with children nested within children?
Starting Data (2 or more objects):
[
{
id:1 ,
name: "hot-dog",
children: [
{name:"meat"}
]
},
{
id:2 ,
name: "meat",
children: [
{name:"salt"}
{name:"horse-meat"}
]
}
]
Desired Data (one master object):
[
{
id:1 ,
name: "hot-dog",
children: [
{
id:2 ,
name: "meat",
children: [
{name:"salt"}
{name:"horse-meat"}
]
}
]
}
]

It seems that you want to rearrange those items to be displayed as a tree view.
As you've posted, each of then are displayed as objects in a flat array, like the following:
[
{ id: 1, name: "hot-dog",
children: [ { name: "meat" } ] },
{ id: 2, name: "meat",
children: [ { name: "salt" }, { name: "horse-meat" } ] },
{ id: 3, name: "salt" },
{ id: 4, name: "horse-meat" },
];
And need to be transformed into a array of master objects.
I've written a MRE that can display it working. Also, see this working example on codepen.
<div id="app">
<v-app id="inspire">
<v-treeview :items="formattedItems"></v-treeview>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{
id: 1,
name: "hot-dog",
children: [
{name:"meat"},
],
},
{
id: 2,
name: "meat",
children: [
{name:"salt"},
{name:"horse-meat"},
],
},
{
id: 3,
name: "pancake",
children: [
{name: "honey"},
]
},
{
id: 4,
name: "honey",
},
{
id: 5,
name: "salt",
},
{
id: 6,
name: "horse-meat",
},
]
}),
computed: {
formattedItems() {
const mapper = function (current) {
let child = this.find(item => item.name === current.name);
if (child.children) {
child.children = child.children.map(mapper, this);
}
return child;
};
const reducer = (accum, current, index, array) => {
if (!array.find(item => !!item.children && item.children.find(child => child.name === current.name))) {
current.children = current.children.map(mapper, array);
accum.push(current);
}
return accum;
};
return this.items.reduce(reducer, []);
}
}
})

Related

fillter arrays of objects

i have two arrays.
const department = [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
];
const models = [
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }],
},
{
id: '54',
name: 'model2',
departments: [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
],
},
];
i need to render accordions with department names and accordion details with matching models names. My question is how to filter those arrays to get models
We can map through the departments array, and add a models property that equals the models array, but filtered only to the ones that contain a matching department id.
const departments = [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
];
const models = [
{
id: "23",
name: "model1",
departments: [{ id: "1", name: "department1" }],
},
{
id: "54",
name: "model2",
departments: [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
],
},
];
const getDepartmentsWithModels = () => {
return departments.map((department) => {
return {
...department,
models: models.filter((model) => {
const modelDepartmentIds = model.departments.map(({ id }) => id);
return modelDepartmentIds.includes(department.id);
}),
};
});
};
console.log(getDepartmentsWithModels());
// [ { id: '1', name: 'department1', models: [ [Object], [Object] ] },
// { id: '2', name: 'department2', models: [ [Object] ] } ]```
I've built some code, which iterates over the departments. For each department it iterates the models and for each model it checks if the department is within the model departments.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }]
},
{
id: '54',
name: 'model2',
departments: [{ id: '1', name: 'department1' },{ id: '2', name: 'department2' }]
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.forEach(model => {
if (model.departments.find(modelDep => dep.id===modelDep.id)) {
console.log(` Model: ${model.name}`)
}
})
})
If you could change your data objects, then your code could be much smoother.
I've changed your data objects slightly by just reducing the departments in a model to be an array of department id's. This code iterates over the departments. For each department it filters the models and iterates over the filtered models to output them to the console. This is lesser code and provides much better performance.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: ['1']
},
{
id: '54',
name: 'model2',
departments: ['1', '2']
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.filter(model => model.departments.includes(dep.id)).forEach(model => {
console.log(` Model: ${model.name}`)
})
})
There are two solutions.
Using Array.reduce() --> returns an object where the key is department name and value is an array of the names of matching models:
let data1 = models.reduce((res, curr) => {
curr.departments.forEach(dep => {
if (!res[dep.name]) {
res[dep.name] = [curr.name]
} else {
if (!res[dep.name].includes(curr.name)) {
res[dep.name].push(curr.name);
}
}
})
return res;
}, {});
Using map and filter --> returns an array of kind:
[{department: [names of the models]},...]
let data2 = department.map(dep => {
let matchingModels = models.filter(model => {
return model.departments.filter(modDep => {
return modDep.name === dep.name;
}).length > 0;
}).map(mod => {
return mod.name;
});
return {
department: dep.name,
models: matchingModels
}
});

How to convert tree having nodes stored as key-value objects to a tree structure having nodes stored as array of objects in Javascript?

I am trying to convert a tree structure having nodes stored as key-value objects to a tree structure having nodes stored as array of objects. How could I achieve it?
Example: I would like to convert this structure:
const treeX = [
{
id: '7d2a730a-53b7-4ce6-b791-446a7dc3d97e',
name: 'category1',
nodes: {
'd043f4bd-6d3f-478c-b4ea-a5fc1ad66fb1': {
name: 'subcategory2',
id: 'd043f4bd-6d3f-478c-b4ea-a5fc1ad66fb1',
nodes: {
'69c7481d-d8c3-41da-b8fa-84d6056f6344': {
name: 'subsubcategory3',
id: '69c7481d-d8c3-41da-b8fa-84d6056f6344',
nodes: {},
},
'184e0b3d-108c-45ee-8c62-a81b73bf5b7b': {
name: 'subsubcategory4',
id: '184e0b3d-108c-45ee-8c62-a81b73bf5b7b',
nodes: {},
},
},
},
'0ec9a897-6e06-4c06-a780-b37ce8fd51f7': {
name: 'subcategory5',
id: '0ec9a897-6e06-4c06-a780-b37ce8fd51f7',
nodes: {
'9531f8d4-0d28-4a89-8d4e-ddd3ceea4939': {
name: 'subsubcategory6',
id: '9531f8d4-0d28-4a89-8d4e-ddd3ceea4939',
nodes: {},
},
'2f32f5b8-0abb-48e1-b6ad-b7f00aba02ca': {
name: 'subsubcategory7',
id: '2f32f5b8-0abb-48e1-b6ad-b7f00aba02ca',
nodes: {},
},
},
},
},
},
{
id: '985ab20a-53b7-4ce6-b791-446a7dc3d97e',
name: 'category8',
nodes: {
'26b97abd-6d3f-478c-b4ea-a5fc1ad66fb1': {
name: 'subcategory9',
id: '236b45bd-6d3f-478c-b4ea-a5fc1ad66fb1',
nodes: {},
},
},
},
];
to:
const treeY = [
{
id: '7d2a730a-53b7-4ce6-b791-446a7dc3d97e',
name: 'category1',
nodes: [
{
name: 'subcategory2',
id: 'd043f4bd-6d3f-478c-b4ea-a5fc1ad66fb1',
nodes: [
{
name: 'subsubcategory3',
id: '69c7481d-d8c3-41da-b8fa-84d6056f6344',
nodes: [],
},
{
name: 'subsubcategory4',
id: '184e0b3d-108c-45ee-8c62-a81b73bf5b7b',
nodes: [],
},
],
},
{
name: 'subcategory5',
id: '0ec9a897-6e06-4c06-a780-b37ce8fd51f7',
nodes: [
{
name: 'subsubcategory6',
id: '9531f8d4-0d28-4a89-8d4e-ddd3ceea4939',
nodes: [],
},
{
name: 'subsubcategory7',
id: '2f32f5b8-0abb-48e1-b6ad-b7f00aba02ca',
nodes: [],
},
],
},
],
},
{
id: '985ab20a-53b7-4ce6-b791-446a7dc3d97e',
name: 'category8',
nodes: [
{
name: 'subcategory9',
id: '236b45bd-6d3f-478c-b4ea-a5fc1ad66fb1',
nodes: [],
},
],
},
];
The way how I've achieved it:
categories.forEach(category => {
category.nodes = Object.values(category.nodes);
category.nodes?.forEach(subcategory => {
subcategory.nodes = Object.values(subcategory.nodes);
subcategory.nodes?.forEach(subsubcategory => {
subsubcategory.nodes = Object.values(subsubcategory.nodes);
subsubcategory.nodes?.forEach(subsubsubcategory => {
subsubsubcategory.nodes = Object.values(subsubsubcategory.nodes)
})
})
})
});
I know that the solution is absolutely uneffective and it won't work if the tree would have more levels. Therefore, I would like to ask, how can I improve the code? Maybe using recursive function?
Looks to me all you really need is to recursively map the Object.values of the nodes property:
const treeX=[{id:"7d2a730a-53b7-4ce6-b791-446a7dc3d97e",name:"category1",nodes:{"d043f4bd-6d3f-478c-b4ea-a5fc1ad66fb1":{name:"subcategory2",id:"d043f4bd-6d3f-478c-b4ea-a5fc1ad66fb1",nodes:{"69c7481d-d8c3-41da-b8fa-84d6056f6344":{name:"subsubcategory3",id:"69c7481d-d8c3-41da-b8fa-84d6056f6344",nodes:{}},"184e0b3d-108c-45ee-8c62-a81b73bf5b7b":{name:"subsubcategory4",id:"184e0b3d-108c-45ee-8c62-a81b73bf5b7b",nodes:{}}}},"0ec9a897-6e06-4c06-a780-b37ce8fd51f7":{name:"subcategory5",id:"0ec9a897-6e06-4c06-a780-b37ce8fd51f7",nodes:{"9531f8d4-0d28-4a89-8d4e-ddd3ceea4939":{name:"subsubcategory6",id:"9531f8d4-0d28-4a89-8d4e-ddd3ceea4939",nodes:{}},"2f32f5b8-0abb-48e1-b6ad-b7f00aba02ca":{name:"subsubcategory7",id:"2f32f5b8-0abb-48e1-b6ad-b7f00aba02ca",nodes:{}}}}}},{id:"985ab20a-53b7-4ce6-b791-446a7dc3d97e",name:"category8",nodes:{"26b97abd-6d3f-478c-b4ea-a5fc1ad66fb1":{name:"subcategory9",id:"236b45bd-6d3f-478c-b4ea-a5fc1ad66fb1",nodes:{}}}}];
const transformObj = (obj) => ({
...obj,
nodes: Object.values(obj.nodes).map(transformObj)
});
const result = treeX.map(transformObj);
console.log(result);

ReactJS - Swap out all the values in an array of objects in state with another array

I am trying to swap out all the values of an array of objects in state with a whole new array of objects. However, nothing seems to be working. I've tried the following:
const list1 = [
{ id: 1, name: 'item1' },
{ id: 2, name: 'item1' },
{ id: 3, name: 'item1' },
{ id: 4, name: 'item1' },
]
const list2 = [
{ id: 1, name: 'newItem1' },
{ id: 2, name: 'newItem2' },
{ id: 3, name: 'newItem3' },
{ id: 4, name: 'newItem4' },
]
class FindTab extends Component {
state = {
status: 'loading',
location: null,
view: this.props.view,
map: this.props.map,
locationValues: list1,
}
}
this.setState(prevState => ({
locationValues: [ ...prevState.locationValues, list2 ],
}))
or just simpler:
this.setState(locationValues: list2)
Neither seem to work. Is there any guidance as to how one should replace an array of objects with another array for a state property?
You could spread the array in a new one like:
const locationValues = [ ...state.locationValues, ...list2 ]
this.setState({ locationValues })

Nested array filtering, is there a more elegant way?

I'm trying to filter a nested structure, based on a search string.
If the search string is matched in an item, then I want to keep that item in the structure, along with its parents.
If the search string is not found, and the item has no children, it can be discounted.
I've got some code working which uses a recursive array filter to check the children of each item:
const data = {
id: '0.1',
children: [
{
children: [],
id: '1.1'
},
{
id: '1.2',
children: [
{
children: [],
id: '2.1'
},
{
id: '2.2',
children: [
{
id: '3.1',
children: []
},
{
id: '3.2',
children: []
},
{
id: '3.3',
children: []
}
]
},
{
children: [],
id: '2.3'
}
]
}
]
};
const searchString = '3.3';
const filterChildren = (item) => {
if (item.children.length) {
item.children = item.children.filter(filterChildren);
return item.children.length;
}
return item.id.includes(searchString);
};
data.children = data.children.filter(filterChildren);
console.log(data);
/*This outputs:
{
"id": "0.1",
"children": [
{
"id": "1.2",
"children": [
{
"id": "2.2",
"children": [
{
"id": "3.3",
"children": []
}
]
}
]
}
]
}*/
I'm concerned that if my data structure becomes massive, this won't be very efficient.
Can this be achieved in a 'nicer' way, that limits the amount of looping going on? I'm thinking probably using a reducer/transducer or something similarly exciting :)
A nonmutating version with a search for a child.
function find(array, id) {
var child,
result = array.find(o => o.id === id || (child = find(o.children, id)));
return child
? Object.assign({}, result, { children: [child] })
: result;
}
const
data = { id: '0.1', children: [{ children: [], id: '1.1' }, { id: '1.2', children: [{ children: [], id: '2.1' }, { id: '2.2', children: [{ id: '3.1', children: [] }, { id: '3.2', children: [] }, { id: '3.3', children: [] }] }, { children: [], id: '2.3' }] }] },
searchString = '3.3',
result = find([data], searchString);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript filtering nested arrays

I'm trying to filter a on a nested array inside an array of objects in an Angular app. Here's a snippet of the component code -
var teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
What I'm trying to achieve is if I search for m5 for example my result should be -
var teams = [
{ name: 'Team1', members: [] },
{ name: 'Team2', members: [{ name: 'm5' }] },
{ name: 'Team3', members: [] }
];
So I've got teams and filteredTeams properties and in my search function I'm doing -
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = this.teams.map(t => {
t.members = t.members.filter(d => d.name.toLowerCase().includes(value));
return t;
})
}
Now this does work to some extent however because I'm replacing the members it's destroying the array on each call (if that makes sense). I understand why this is happening but my question is what would be the best way to achieve this filter?
you were very close, the only thing that you did wrong was mutating the source objects in teams
basically you can use spread operator to generate a new entry and then return a whole new array with new values.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const value = 'm5';
const result = teams.map(t => {
const members = t.members.filter(d => d.name.toLowerCase().includes(value));
return { ...t, members };
})
console.log(result)
Check this. Instead of hard coded m5 pass your value.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const filteredTeams = teams.map(team => ({ name: team.name, members: team.members.filter(member => member.name.includes('m5')) }));
console.log(filteredTeams);
You are mutating the original objects, but you could assing new properties to the result object for mapping instead.
var teams = [{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] }, { name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] }, { name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }],
result = teams.map(o => Object.assign(
{},
o,
{ members: o.members.filter(({ name }) => name === 'm5') }
));
console.log(result);
console.log(teams);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try to seperate your filter function first:
const filterTeamMembers = (teams, filterArr) => {
const useFilter = filterArr.map(x => x.toLowerCase());
return teams.map(team => ({
...team,
members: team.members.filter(member => useFilter.includes(member.name))
}))
};
// =========== And then:
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = filterTeamMembers(this.teams, [value]);
}

Categories

Resources