You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
40 lines
1.8 KiB
40 lines
1.8 KiB
import { ApiResponse, Sitemap } from './types';
|
|
|
|
type Subtree = {
|
|
name: string;
|
|
id: number;
|
|
children: Sitemap[];
|
|
};
|
|
|
|
// alternatively, for full TS experience, if I had more time I'd also implement parsing in TS so that this declaration would look like:
|
|
// export const createSitemap = <TResponse extends ApiResponse>(apiResponse: TResponse): Sitemap<TResponse> => {
|
|
// and calling e.g. `createSitemap(const sample data json)` would return value with type `const sample sitemap`, rather than just general sitemap
|
|
export const createSitemap = (apiResponse: ApiResponse): Sitemap | null => {
|
|
const subtrees = new Map<number, Subtree>(
|
|
apiResponse.map(({ id, slug }) => [
|
|
id,
|
|
{ id, name: slug, children: [] },
|
|
]),
|
|
);
|
|
let root: Subtree | null = null;
|
|
for (const entry of apiResponse) {
|
|
if (entry.parent === null) {
|
|
if (root) {
|
|
throw new Error(
|
|
`Multiple root nodes found (${root.id} and ${entry.id})`,
|
|
);
|
|
}
|
|
// We can be sure that `get` returns value: `entry.id` exists because that's how we initialized subtrees in the beginning
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
root = subtrees.get(entry.id)!;
|
|
} else if (subtrees.has(entry.parent)) {
|
|
// We can be sure that both `get`s return values because:
|
|
// 1. We just checked that `entry.parent` is an existing id;
|
|
// 2. `entry.id` exists because that's how we initialized subtrees in the beginning
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
subtrees.get(entry.parent)!.children.push(subtrees.get(entry.id)!);
|
|
}
|
|
}
|
|
|
|
return root;
|
|
};
|
|
|