In my vue app, I have an array of objects which I want to render as a list. Within each object, there is a 'parent' property which contains info about who the parent is thereby giving us a hierarchical data structure. However, I am unable to show this hierarichal list. I tried creatin a tree by transforming the data but my code seems bugged.
You can see my attempted version here: http://jsbin.com/pewusiyete/edit?html,js,console,output
Code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="item in list">
<a v-bind:href="item.value">{{item.label}}</a>
</li>
</ul>
{{tree}}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
list:[
{
"label":"Parks & Gardens",
"type":"place",
"value":"parks-gardens",
"id":"0CNYF4qh0aaYi2XLGCfG"
},
{
"label":" Art & Craft",
"type":"product",
"value":"art-craft",
"id":"4TfXwraLAJX9K5WeIBT5"
},
{
"label":"Monuments",
"type":"place",
"value":"monuments",
"id":"4mVxy4QEplxTnf6NwpIf"
},
{
"label":"Books",
"type":"book",
"value":"books",
"id":"4oVqbEDhPSYqaTDn4xMV"
},
{
"label":"Sports Academies",
"type":"place",
"value":"sports-academies",
"id":"H7GkAF0Hfdu3OoHBYyQY"
},
{
"label":"Store",
"type":"place",
"value":"store",
"id":"Ki4YjRNe4HmZWOCOpg9K"
},
{
"label":"Tennis Academies",
"parent":"sports-academies",
"type":"place",
"value":"tennis-academies",
"id":"N89adEZwHfSGMQiq1R5f"
},
{
"label":"Toy Stores",
"type":"place",
"parent":"stores",
"value":"toy-stores",
"id":"Oj6QgO0S0Z6AFHvrr2ZH"
},
{
"label":"Electronics",
"type":"product",
"value":"electronics",
"id":"UuztFKZrsw3vcciKaj8k"
},
{
"label":"Tech",
"type":"product",
"value":"tech",
"id":"WuqiVSXxmlCCQ5usAUNZ"
},
{
"label":"Book Stores",
"type":"place",
"parent":"stores",
"value":"book-stores",
"id":"ZmXlJ12jJROGeHjYcwOT"
},
{
"label":" Online Stores",
"type":"commerce",
"value":"online-stores",
"id":"cIRSVPcqSDr6WfuRe4OX"
},
{
"label":"Play Areas",
"type":"place",
"value":"play-areas",
"id":"fEk3dcKprq9Hd8rSgiG3"
},
{
"label":"Toys",
"type":"product",
"value":"toys",
"id":"rJTpw2V9apxe9jQjLTOS"
},
{
"label":"Stores",
"type":"place",
"value":"stores",
"id":"ZmXlJ12jJROGeHjYcwOH"
}
]
},
computed: {
newitem: function () {
return this.list.reduce(function(p,c) {
p[c.value] = c;
c.children = [];
return p;
}, {});
},
tree: function () {
return this.list.reduce(function(p,c) {
console.log(c.parent)
if (c.parent = 'undefined') {
p = c;
} else {
newitem[c.parent].children.push(c);
}
return p;
}, {});
}
}
});
</script>
</body>
</html>
There's a few things wrong with your code, I won't go into too much detail, but at a high level:
if (c.parent = 'undefined') should probably be if (c.parent === undefined).
newitem[c.parent].children.push(c) should probably be this.newitem[c.parent].children.push(c), but even then you shouldn't mutate computed properties.
I think just overall you are not structuring your menu items properly.
First you will need to transform the flat list of items into a tree structure. Then you will need to use recursive components in order to render such a tree.
Here's an example:
const MenuList = {
name: 'menu-list',
template: '#menu-list-template',
props: ['items'],
};
new Vue({
el: '#app',
components: {
MenuList,
},
data: {
list: [
{
"label": "Parks & Gardens",
"type": "place",
"value": "parks-gardens",
"id": "0CNYF4qh0aaYi2XLGCfG"
},
{
"label": " Art & Craft",
"type": "product",
"value": "art-craft",
"id": "4TfXwraLAJX9K5WeIBT5"
},
{
"label": "Monuments",
"type": "place",
"value": "monuments",
"id": "4mVxy4QEplxTnf6NwpIf"
},
{
"label": "Books",
"type": "book",
"value": "books",
"id": "4oVqbEDhPSYqaTDn4xMV"
},
{
"label": "Sports Academies",
"type": "place",
"value": "sports-academies",
"id": "H7GkAF0Hfdu3OoHBYyQY"
},
{
"label": "Store",
"type": "place",
"value": "store",
"id": "Ki4YjRNe4HmZWOCOpg9K"
},
{
"label": "Tennis Academies",
"parent": "sports-academies",
"type": "place",
"value": "tennis-academies",
"id": "N89adEZwHfSGMQiq1R5f"
},
{
"label": "Toy Stores",
"type": "place",
"parent": "stores",
"value": "toy-stores",
"id": "Oj6QgO0S0Z6AFHvrr2ZH"
},
{
"label": "Electronics",
"type": "product",
"value": "electronics",
"id": "UuztFKZrsw3vcciKaj8k"
},
{
"label": "Tech",
"type": "product",
"value": "tech",
"id": "WuqiVSXxmlCCQ5usAUNZ"
},
{
"label": "Book Stores",
"type": "place",
"parent": "stores",
"value": "book-stores",
"id": "ZmXlJ12jJROGeHjYcwOT"
},
{
"label": " Online Stores",
"type": "commerce",
"value": "online-stores",
"id": "cIRSVPcqSDr6WfuRe4OX"
},
{
"label": "Play Areas",
"type": "place",
"value": "play-areas",
"id": "fEk3dcKprq9Hd8rSgiG3"
},
{
"label": "Toys",
"type": "product",
"value": "toys",
"id": "rJTpw2V9apxe9jQjLTOS"
},
{
"label": "Stores",
"type": "place",
"value": "stores",
"id": "ZmXlJ12jJROGeHjYcwOH"
}
]
},
computed: {
treeList() {
// Deep clone the list and add a children property with empty array value to each item
const items = this.list.map(item => Object.assign({}, item, { children: [] }));
// Organize items into a map keyed by item value for easy lookup
const byValue = new Map(items.map(item => [item.value, item]));
// Top level will contain the items which do not have a parent
const topLevel = [];
for (const item of items) {
// Look up the parent item if there is one
const parent = byValue.get(item.parent);
if (parent) {
// Append the item into the parent's children array
parent.children.push(item);
} else {
// The item has no parent
topLevel.push(item);
}
}
return topLevel;
}
}
});
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>
<div id="app">
<menu-list :items="treeList"></menu-list>
</div>
<script type="text/x-template" id="menu-list-template">
<ul v-if="items.length">
<li v-for="item of items">
<a :href="item.value">{{ item.label }}</a>
<menu-list :items="item.children"></menu-list>
</li>
</ul>
</script>
Related
Trying to get my head around this one..
Incoming data looks like:
[
{
"value": {
"label": "MZ Algal bloom",
"type": "case",
"incident": {
"name": "Algal bloom"
},
"personName": "Lionel Carter"
}
},
{
"value": {
"label": "BW Algal bloom",
"type": "case",
"incident": {
"name": "Algal bloom"
},
"personName": "Jerome Yost"
}
},
{
"value": {
"label": "Detergent",
"type": "case",
"incident": null,
"personName": "Jerald Legros"
}
}
]
I would like to transform this into
[
{
"label": "Algal bloom",
"children": [
{ "label": "Lionel Carter", "type": "case"},
{ "label": "Jerome Yost", "type": "case" }]
},
{ "label": "Detergent", "type": "case" }
]
Basically, the rule is that if incident is not NULL then the incident name becomes the parent and the children hold the personName - otherwise we simply pass through the label and type. I can walk the array and switch out the label with the incident name, but I'm not sure how to group up the incidents..
It's basic grouping with an exception for elements without incident.
You can group the elements without incident in a separate group:
const data = [{"value": {"label": "MZ Algal bloom","type": "case","incident": {"name": "Algal bloom"},"personName": "Lionel Carter"}},{"value": {"label": "BW Algal bloom","type": "case","incident": {"name": "Algal bloom"},"personName": "Jerome Yost"}},{"value": {"label": "Detergent","type": "case","incident": null,"personName": "Jerald Legros"}}];
function group(data) {
const result = data.reduce((acc, { value }) => {
if (!value.incident) {
acc.ungrouped.push({ label: value.label, type: value.type });
} else {
if (!acc.groups[value.incident.name]) acc.groups[value.incident.name] = { label: value.incident.name, children: [] };
acc.groups[value.incident.name].children.push({ label: value.personName, type: value.type });
}
return acc;
}, { groups: {}, ungrouped: [] });
return [...Object.values(result.groups), ...result.ungrouped];
}
console.log(group(data));
Hya 👋
Suppose we have a dynamic object like so:
[
{
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"parent": {
"type": "page_id",
"page_id": "d4b96daf-47a3-4a04-b200"
},
"type": "child_database",
"child_database": {
"title": "Hero"
}
},
{
"object": "page",
"id": "d3022361-96d2-4e15-999e",
"parent": {
"type": "database_id",
"database_id": "089cd0d8-ccbf-4e9e-97a6"
},
},
{
"object": "block",
"id": "a0cba166-1787-4e30-8cc3",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_1",
"heading_1": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}
],
}
},
{
"object": "block",
"id": "dbfdd892-8c04-4de3-bf0e",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_2",
"heading_2": {
"rich_text": [
{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}
],
}
}
]
I would like to reconstruct this object by grouping them based on parent-child like relationship. Since every object has "parent" prop.
The desired result should be like so, where the elements that share the same parent are grouped under child array.
{
"d4b96daf-47a3-4a04-b200": {
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"type": "child_database",
"child": [{
"d3022361-96d2-4e15-999e": {
"object": "page",
"child": [{
"a0cba166-1787-4e30-8cc3": {
"object": "block",
"type": "heading_1",
"heading_1": {
"rich_text": [{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}]
}
}
},
{
"dbfdd892-8c04-4de3-bf0e": {
"object": "block",
"type": "heading_1",
"heading_2": {
"rich_text": [{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}]
}
}
}
]
}
}]
}
}
Current workaround
/**
* Generator that traverses through nested object
*/
function* traverse(xs: any[] = []): any {
for (let x of xs) {
yield x
yield* traverse(x.child || [])
}
}
/**
* If the property exists in the nested object, then return node
*/
const deepFind = (block: any, pred: any) => (obj: any) => {
for (let node of traverse([obj])) {
if (pred(node)) {
return node
}
}
}
const findById = (block: any) => (obj: any) => deepFind(block, (o: any) => o[block.id])(obj)
export default async function group(pages: Page[]) {
// stuck here 🙏
}
You can do this linearly: create a Map id=>object, iterate the list, if the parent is already on the map, add your object to the parent.child, otherwise create a placeholder object with the parent's id.
let m = new Map()
for (let obj of data) {
let dummy = {id: 'dummy', child: []}
let oid = obj.id
m.set(oid, {...dummy, ...obj, ...m.get(oid)})
let pid = obj.parent.page_id // or whatever depending on type
m.set(pid, m.get(pid) ?? dummy)
m.get(pid).child.push(obj)
}
In the end, the m.values() will contain a flat list of objects with child arrays properly populated.
I have a huge json which I am fetching from Excel sheet.
Data I am getting as array of objects and one object looks like below.
[
{
"key": "guid",
"parent": "id__guid"
},
{
"key": "version",
"parent": "id__version"
},
{
"key": "register",
"parent": "register"
},
{
"key": "offloadId",
"parent": "offloadId"
},
{
"key": "action",
"parent": "action"
},
{
"key": "reported",
"parent": "reported"
},
{
"key": "control",
"parent": "control"
},
{
"key": "AppNum",
"parent": "Identification__AppNum"
},
{
"key": "DataTp",
"parent": "Identification__DataTp"
},
{
"key": "DtOgWatchDt",
"parent": "Identification__DtOgWatchDt"
},
{
"key": "DtPendingWatchDt",
"parent": "Identification__DtPendingWatchDt"
},
{
"key": "IssRef",
"parent": "Identification__IssRef"
},
{
"key": "ImgRef",
"parent": "Identification__ImgRef"
},
{
"key": "Register",
"parent": "Identification__Register"
},
{
"key": "-",
"parent": "Identification__ImgRefFullPub"
},
{
"key": "DtAppDt",
"parent": "Dates__DtAppDt"
},
{
"key": "IdxNam",
"parent": "Description__IdxNam"
},
{
"key": "Clms",
"parent": "Description__Clms"
},
{
"key": "Disclaims",
"parent": "Description__Disclaims"
},
{
"key": "LglStsCd",
"parent": "Status__LglStsCd"
},
{
"key": "UsPtoStsCd",
"parent": "Status__UsPtoStsCd"
},
{
"key": "PtoStsCdDt",
"parent": "Status__PtoStsCdDt"
},
{
"key": "StsFlag",
"parent": "Status__StsFlag"
},
{
"key": "SrcInd",
"parent": "Status__SrcInd"
},
{
"key": "LglStsCdNorm",
"parent": "Status__LglStsCdNorm"
}
]
I want to convert it into this format which is nested json.
[
{
name: "Identification",
fields: [
{
"key": "AppNum",
"parent": "Identification__AppNum"
},
{
"key": "DataTp",
"parent": "Identification__DataTp"
},
{
"key": "DtOgWatchDt"
},
{
"key": "DtPendingWatchDt"
},
{
"key": "IssRef"
},
{
"key": "ImgRef"
},
{
"key": "Register"
},
{
"key": "ImgRefFullPub"
},
{
"key": "guid"
},
{
"key": "version"
},
{
"key": "offloadid"
},
{
"key": "reported"
},
{
"key": "control"
}
]
},
{
name: "Description",
fields: [
{
"key": "IdxNam"
},
{
"key": "Clms"
},
{
"key": "Disclaims"
}
]
},
{
name: "Status",
fields: [
{
"key": "UsPtoStsCd"
},
{
"key": "PtoStsCdDt"
},
{
"key": "LglStsCd"
},
{
"key": "StsFlag"
},
{
"key": "SrcInd"
},
{
"key": "LglStsCdNorm"
}
]
},
{
name: "Dates",
fields: [
{
"key": "DtAppDt"
}
]
}
]
As you can see according to parent key we have to create nested structure.
I have tried all the ways, I search a lot on google also, but hard luck.
Any help will be appreciated.
You'll want to create a new array of objects, then loop through the original array and add entries to the new array based on that. For example --
const input = [
{
"key": "guid",
"parent": "id__guid"
},
{
"key": "version",
"parent": "id__version"
}
// et cetera... your input
];
// these are buckets.
const transformedObject = {
"Identification": []
}
// First we put the data into the right buckets
input.forEach((entry) => {
// Create a new object with the key
const newObject = { key: entry.key };
// By default, the parent seems to be "Identification"
let parentKey = "Identification";
// Find out if the parent's name has an underscore?
if (entry.parent.split("__").length > 1) {
// If so, that's the new parent
parentKey = entry.parent.split("__")[0];
}
// If there isn't an array for this parent, make one
if (!transformedObject[parentKey]) {
transformedObject[parentKey] = [];
}
transformedObject[parentKey].push(newObject);
})
const output = [];
// Then we need to shape the data
Object.keys(transformedObject).forEach((parentKey) => {
const parentGroup = {
name: parentKey,
fields: transformedObject[parentKey]
};
output.push(parentGroup);
});
console.log(output);
You'll notice this doesn't get you all the way there. The "id" prefix seems to be merged into the "Identification" prefix, and you want to keep the parent value on some of these objects. You'll need some conditionals or a map or something to get that part working. But I hope this is a start!
I have an Array of Objects, each containing Array and Objects, like so:
data = [{
"id": 10022,
"date": "2017-12-31T03:44:19.963808Z",
"bought_beats": [{
"id": 10034,
"beat": {
"id": 6334,
"name": "Glass",
"producer": {
"id": 23,
"display_name": "MadReal",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}, {
"id": 894,
"beat": {
"id": 6334,
"name": "Other Name",
"producer": {
"id": 25,
"display_name": "Other Name",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}]
}, {
"moredata": "stuff"
}]
And I need to filter the bought_beats property, and only return beat, if beat.producer.id === 23
This is what I have but it's clearly not working
data.forEach(order => {
return order.bought_beats.filter(item => item.beat.id === producerId)
})
===========
Edit1:
Trying this. It "works", but it also removed some properties (id & date) from each order object (which is each index of data), so I have objects that only contain the array of "bought_beats"
var res = data.map(item => item.bought_beats.filter(item => item.beat.producer.id === 23))
========
Edit2
This seems to be 1 solution, it maintains the array and object structure the same, while it removes those unwanted elements from the bought_beats array.
data.forEach(order => {
let elementToRemoveIndex = order.bought_beats.findIndex(item => item.beat.producer.id !== 23)
order.bought_beats.splice(elementToRemoveIndex, 1)
})
Thanks #Pac0 for the continuous help
use .find over data.bought_beats since its an array,
DEMO
var data = [{
"id": 10022,
"date": "2017-12-31T03:44:19.963808Z",
"bought_beats": [{
"id": 10034,
"beat": {
"id": 6334,
"name": "Glass",
"producer": {
"id": 23,
"display_name": "MadReal",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}, {
"id": 894,
"beat": {
"id": 6334,
"name": "Other Name",
"producer": {
"id": 25,
"display_name": "Other Name",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}]
}, {
"moredata": "stuff"
}];
var result = data.find(dat => dat.bought_beats.some(item => item.beat.producer.id === 23));
console.log(result);
If I understood correctly, this should be what you want :
// project each object to its bought_beats / beats part
var beatsArrays = data.filter(x => x.bought_beats).map(x => x.bought_beats);
// flatten the array of arrays of beats into a simple array of beats
var beats = [].concat.apply([],beatsArrays).map(x => x.beat);
// filter
var relevantBeats = beats.filter(item => item.producer.id === 23);
// serve with a cherry in a sugar-frost cocktail glass (happy new year ! )
console.log(relevantBeats);
Snippet :
data = [{
"id": 10022,
"date": "2017-12-31T03:44:19.963808Z",
"bought_beats": [{
"id": 10034,
"beat": {
"id": 6334,
"name": "Glass",
"producer": {
"id": 23,
"display_name": "MadReal",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}, {
"id": 894,
"beat": {
"id": 6334,
"name": "Other Name",
"producer": {
"id": 25,
"display_name": "Other Name",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}]
}, {
"moredata": "stuff"
}];
// project each object to its bought_beats / beats part
var beatsArrays = data.filter(x => x.bought_beats).map(x => x.bought_beats);
// flatten the array of arrays of beats into a simple array of beats
var beats = [].concat.apply([],beatsArrays).map(x => x.beat);
// filter
var relevantBeats = beats.filter(item => item.producer.id === 23);
// serve with a cherry in a sugar-frost cocktail glass (happy new year ! )
console.log(relevantBeats);
// for each order
data.forEach(order => {
// we loop thorugh the bought beats array
order.bought_beats.forEach((item, index) => {
// and if there's a beat from another producer, we remove it
if (item.beat.producer.id !== producerId) order.bought_beats.splice(index, 1)
})
})
I have 2 array of object, one is the preloaded list, one is the selected items. My problem is couldn't make the selected items checked on checkboxes.
https://jsfiddle.net/8usvfzv9
class HelloWidget extends React.Component {
constructor(props) {
super(props);
this.list = [{
"id": "exhibitions",
"name": "Exhibitions"
}, {
"id": "festivals_n_concerts",
"name": "Festivals & Concerts"
}, {
"id": "grand_opening",
"name": "Grand Opening"
}, {
"id": "meeting",
"name": "Meeting"
}, {
"id": "party",
"name": "Party"
}, {
"id": "product_launches",
"name": "Product Luanches"
}, {
"id": "roadshows",
"name": "Roadshows"
}, {
"id": "sporting_events",
"name": "Sporting Events"
}, {
"id": "trade_show",
"name": "Trade Show"
}]
this.selectedList = [{
"id": "grand_opening",
"name": "Grand Opening",
"space_event_id": "grand_opening"
}, {
"id": "trade_show",
"name": "Trade Show",
"space_event_id": "trade_show"
}]
}
render() {
return (<div>
{this.list.map(obj => <div><br /><input
key={obj.name}
checked={this.selectedList.findIndex(o => o.id === obj.id)}
type="checkbox" >{obj.name}</input></div>)}
</div>
)
}
}
I think this line is wrong
checked={this.selectedList.findIndex(o => o.id === obj.id)}
base on the output result. Any clue how to use findIndex?
As 'checked' prop only works with boolean and findIndex returns number, you can modify as below:
checked={this.selectedList.findIndex(o => o.id === obj.id) !== -1}