toggle-corp/react-store

View on GitHub
v2/Input/TreeInput/utils.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { mapToMap, listToMap, isDefined, Obj } from '@togglecorp/fujs';

export const version = 1;

interface InternalRelation<K> {
    key: K;
    parentKey?: K;
    problematic: boolean;
    children: { [key: string]: K };
}

export interface Relation<K> {
    key: K;
    parentKey?: K;
    problematic: boolean;
    children: K[];
}

export interface ExtendedRelation<T, K> {
    key: K;
    parentKey?: K;
    problematic: boolean;
    children: T[];
}

export function generateRelations<T, K extends string | number | boolean>(
    options: T[],
    keySelector: (item: T) => K,
    parentKeySelector: (item: T) => K | undefined,
): Obj<Relation<K> | undefined> {
    const acc: {
        [key: string]: InternalRelation<K> | undefined;
    } = {};

    options.forEach((node) => {
        // NOTE: item in acc
        const id = keySelector(node);

        const elementFromAcc = acc[String(id)];
        const parentKey = parentKeySelector(node);
        const elem: InternalRelation<K> = elementFromAcc
            ? ({
                ...elementFromAcc,
                parentKey,
                problematic: false,
            })
            : ({
                key: id,
                parentKey,
                problematic: false,
                children: {},
            });
        acc[String(id)] = elem;

        // Iterate over all parents
        let parentId = parentKeySelector(node);
        while (parentId) {
            const parentFromAcc = acc[String(parentId)];
            const parent: InternalRelation<K> = parentFromAcc
                ? ({
                    ...parentFromAcc,
                    children: {
                        ...parentFromAcc.children,
                        ...elem.children,
                        [String(id)]: id,
                    },
                })
                : ({
                    key: parentId,
                    parentKey: undefined,
                    problematic: true,
                    children: {
                        ...elem.children,
                        [String(id)]: id,
                    },
                });
            acc[String(parentId)] = parent;

            parentId = parent.parentKey;
        }
    });

    return mapToMap(
        acc,
        key => key,
        item => (
            item
                ? ({
                    ...item,
                    children: Object.values(item.children),
                })
                : undefined
        ),
    );
}

export function generateExtendedRelations<T, K extends string | number | boolean>(
    options: T[],
    keySelector: (item: T) => K,
    parentKeySelector: (item: T) => K | undefined,
): Obj<ExtendedRelation<T, K> | undefined> {
    const relations = generateRelations(options, keySelector, parentKeySelector);

    const mapping = listToMap(
        options,
        keySelector,
        item => item,
    );

    return mapToMap(
        relations,
        key => key,
        item => (
            item
                ? ({
                    ...item,
                    children: item.children
                        .map(k => mapping[String(k)])
                        .filter(isDefined),
                })
                : undefined
        ),
    );
}