Getting a recursive data structure asynchronously with RxJS
Tags: typescript, javascript, rxjs
Published: 2019-04-19 11:28:17

---
title: Getting a recursive data structure asynchronously with RxJS
published: true
description: Using RxJS to build a recursive data structure that's not stored as such on a server
tags: typescript, JavaScript, RxJS
cover_image: https://thepracticaldev.s3.amazonaws.com/i/gcsylup57n3n2sa8ckuf.jpg
series: typescript
---

In your app, you might want to save a recursive data structure - such as a folder tree. These folders could be identified by an `id` attribute, and contain an array of their sub-folders:

```typescript
export interface Folder {
  id: number;
  name: string;
  children: Folder[];
}
```

However, on the server the data for each folder might only contain the identifiers for the sub-folders:

```typescript
export interface ServerData {
  id: number;
  name: string;
  children: number[];
}
```

This means that when we write a function to return an observable of a folder, given its `id`, we'll first need to also get all of its sub-folders from the server before we can return the complete object.

Let's look at some mock data, and a function that gets this mock data asynchronously:

```typescript
export const results: ServerData[] = [
  { id: 0, name: "first", children: [1, 2, 3] },
  { id: 1, name: "second", children: [4] },
  { id: 2, name: "third", children: [] },
  { id: 3, name: "fourth", children: [] },
  { id: 4, name: "fifth", children: [] }
];

export function getFromServer(id: number): Observable<ServerData> {
  return of(results[id]);
}
```

Now, let's try to transform the `ServerData` we get into a `Folder`, including its `children` attribute:

```typescript
export function getRecursive(id: number): Observable<Folder> {
  return getFromServer(id).pipe(
    map(data => ({
      id: data.id,
      name: data.name,
      // oops, wrong type!
      children: data.children.map(childId => getRecursive(childId))
    }))
  );
}
```

This implementation doesn't work, because the `children` attribute above is actually an `Observable<Folder>` array. We need to combine the result from getting the parent folder with all of the results of the recursive calls to the ids of the parent's sub-folders.

```typescript
export function getRecursive(id: number): Observable<Folder> {
  return getFromServer(id).pipe(
    map(data => ({
      parent: { name: data.name, id: data.id, children: [] },
      childIds: data.children
    })),
    flatMap(parentWithChildIds => forkJoin([
      of(parentWithChildIds.parent),
      ...parentWithChildIds.childIds.map(childId => getRecursive(childId))
    ])),
    tap(([parent, ...children]) => parent.children = children),
    map(([parent,]) => parent)
  );
}
```

What we do here is create `parent` without any children, and move it and its child ids further along the pipe. Then, we create an array of observables, containing the `parent` we already have (that's the `of` part) and all of the `getRecursive` calls for each of the sub-folder ids. Once we have the return values from each of those recursive calls, we set `parent.children = children`, and use array destructuring to return just the parent folder, which now has its children attribute set correctly.

A short test to show this function in action:

```typescript
describe('test recursive observables', () => {
  test('test', () => {
    getRecursive(0).subscribe(data => {
      console.log(data);
      console.log(data.children.find(f => f.id === 1).children);
    });
  })
})
```

And the output:

```typescript
    parent folder: { name: 'first',
      id: 0,
      children:
       [ { name: 'second', id: 1, children: [Array] },
         { name: 'third', id: 2, children: [] },
         { name: 'fourth', id: 3, children: [] } ] }

    children of child with id 1: [ { name: 'fifth', id: 4, children: [] } ]
```