I have this json data which I need to convert into an object. The json has a parent/child relationship instead of actual children object in the parent object.

{ "queryType": "tree", "queryResultType": "workItemLink", "workItemRelations": [ { "rel": null, "source": null, "target": { "id": 1166167, "url": "https://dev.azure.com/org/_apis/wit/workItems/1166167" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "source": { "id": 1166167, "url": "https://dev.azure.com/org/_apis/wit/workItems/1166167" }, "target": { "id": 1167481, "url": "https://dev.azure.com/org/_apis/wit/workItems/1167481" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "source": { "id": 1167481, "url": "https://dev.azure.com/org/_apis/wit/workItems/1167481" }, "target": { "id": 1234896, "url": "https://dev.azure.com/org/_apis/wit/workItems/1234896" } } ] } 

What I've done so far. Please note that the code below is not the actual code but are snippets I took from the actual code for simplicity.

import axios from 'axios'; interface Item { id: number; children: Item[]; } interface WiqlResponse { queryType: string; queryResultType: string; asOf: string; workItemRelations: Array<{ rel: string; source: { id: number; url: string } | null; target: { id: number; url: string }; }> } export async function getWiql(wiql: string): Promise<WiqlResponse> { const response = await axios.get<WiqlResponse>(`/org/_apis/wit/wiql/${wiql}?api-version=7.1`); return response.data; } export async function main() { getWiql('123123213').then(wiqlResponse => { // Get all parent work items for (const pRel of wiqlResponse.workItemRelations.filter(r => r.rel == null)) { let childs: Item[] = []; // Get all children of this parent for (const cRel of wiqlResponse.workItemRelations.filter(r => r.source?.id == pRel.target.id)) { let child: Item = { id: cRel.target.id, children: [] }; childs.push(child); } let parent: Item = { id: pRel.target.id, children: childs }; } }); } 

The code above will work up to level 2 only but if you look closely at the json data, it has 3 level and could be more depending on the data from the source.

How can I recursively check if there are still child items?

EDIT: Here's a sample code that can be run without axios.

interface Item { id: number; children: Item[]; } interface WiqlResponse { workItemRelations: Array<{ rel: string | null; source: { id: number; url: string } | null; target: { id: number; url: string }; }> } const wiqlResponse: WiqlResponse = { "workItemRelations": [ { "rel": null, "source": null, "target": { "id": 1166167, "url": "https://dev.azure.com/org/_apis/wit/workItems/1166167" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "source": { "id": 1166167, "url": "https://dev.azure.com/org/_apis/wit/workItems/1166167" }, "target": { "id": 1167481, "url": "https://dev.azure.com/org/_apis/wit/workItems/1167481" } }, { "rel": "System.LinkTypes.Hierarchy-Forward", "source": { "id": 1167481, "url": "https://dev.azure.com/org/_apis/wit/workItems/1167481" }, "target": { "id": 1234896, "url": "https://dev.azure.com/org/_apis/wit/workItems/1234896" } } ] }; export async function main() { console.log('wiqlResponse: ', wiqlResponse); let items: Item[] = []; // Get all parent work items for (const pRel of wiqlResponse.workItemRelations.filter(r => r.rel == null)) { let childs: Item[] = []; // Get all children of this parent for (const cRel of wiqlResponse.workItemRelations.filter(r => r.source?.id == pRel.target.id)) { let child: Item = { id: cRel.target.id, children: [] }; childs.push(child); } let parent: Item = { id: pRel.target.id, children: childs }; items.push(parent); } console.log('items:', JSON.stringify(items)); } main(); 

EDIT: Apologies for not explaining the relationship of the json data.

The json provides a relationship between source and target. The target is a top most parent if it doesn't have a source. If the relationship has both source and target, the target is the child of the source.

EDIT: Adding the expected result for clarity. Again my apologies for not being concise the first time.

The expected result should be:

[ { "id": 1166167, "children": [ { "id": 1167481, "children": [ { "id": 1234896, "children": [] } ] } ] } ] 

3 Replies 3

Given your updated example, the following should work:

const itemMap: Record<number, Item> = {} let items: Item[] = [] wiqlResponse.workItemRelations.forEach(i => itemMap[i.target.id] = { id: i.target.id, children: [] }); wiqlResponse.workItemRelations.forEach(i => { const sourceId = i.source?.id; if (!sourceId) { items.push(itemMap[i.target.id]); return; } itemMap[sourceId].children.push(itemMap[i.target.id]) }); console.log('items:', JSON.stringify(items)); 

see this Playground link.

which logs [{"id":1166167,"children":[{"id":1167481,"children":[{"id":1234896,"children":[]}]}]}] as expected.

It basically creates all the items first with empty children, and stores them in a map keyed by id. Then it goes back through the list of relations and looks up the items to augment their children arrays.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.