utils_dataset_remove-rows-by-condition.js

/**
 * @typedef RemoveRowsByCondtionOptions
 * @property {boolean} [mutate=false]     원본을 직접 수정할지 여부 (true면 수정)
 * @property {boolean} [skipEvent=false]  이벤트 활성화 여부, 속도 문제로 기본 값인 false 유지 권장
 */

/**
 * 데이터 저장용 훅
 *
 * @callback HCmbSmrTypeRowConditionUseMemoHook
 * @param {function(): T} func
 * @param {any[]} deps
 * 의존성 키 배열
 * 데이터를 반환하는 함수
 * @template T
 * @example
 * useMemo(() => {
 *     return "data";
 * }, []);
 *
 */

/**
 * @callback RemoveRowsByCondtionFunc
 * @param {RemoveRowByConditionFuncContext} context
 * 참조 함수 및 변수
 * @returns {boolean}
 * true 일 시 삭제
 */

/**
 * @callback FilterRowsByCondtionFunc
 * @param {RemoveRowByConditionFuncContext} context
 * 참조 함수 및 변수
 * @returns {boolean}
 * true 값인 행만 남음
 */

/**
 * @typedef RemoveRowByConditionFuncContext
 * @property {nexacro.Dataset} dataset
 * 대상 데이터셋
 * @property {number} row
 * 행
 * @property {Object} rowData
 * 컬럼 데이터
 * @property {HCmbSmrTypeRowConditionUseMemoHook} useMemo
 * 데이터 기억용
 */

/**
 * 인수로 받은 데이터셋에서 조건을 검사하여 행을 삭제
 *
 * @function removeRowsByCondition
 * @param {nexacro.Dataset} dataset
 * 대상 데이터셋
 * @param {RemoveRowsByCondtionFunc} condition
 * 검사 조건
 * @param {RemoveRowsByCondtionOptions} [options]
 * @returns {nexacro.Dataset | null}
 * 행 삭제 한 데이터셋
 * @example
 * $f.removeRowsByCondition(this.ds_main, (context) => {
 *     return context.row % 2;
 * });
 *
 * @memberOf $f
 */
export function removeRowsByCondition(dataset, condition, options) {
    const isMutate = options?.mutate ?? false;
    const isSkipEvent = options?.skipEvent ?? true;

    const ds = isMutate ? dataset : $ds.clone(dataset);
    const previousDeps = [];
    const memoData = [];
    let initialHookCallIdx = -1;

    if (!(ds instanceof nexacro.Dataset)) {
        return null;
    }

    if (typeof condition !== "function") {
        return ds;
    }

    const defaultEnableEvent = ds.enableevent;

    if (isSkipEvent) {
        ds.enableevent = false;
    }

    for (let i = ds.rowcount - 1; i >= 0; i--) {
        let hookCallIdx = -1;

        /**
         * @type {HCmbSmrTypeRowConditionUseMemoHook}
         */
        const useMemo = function (func, deps) {
            hookCallIdx++;

            if (initialHookCallIdx !== -1 && initialHookCallIdx > hookCallIdx) {
                throw new HookOrderMismatchError();
            }

            let isChanged =
                !previousDeps[hookCallIdx] ||
                deps.length !== previousDeps[hookCallIdx].length ||
                deps.some((dep, i) => dep !== previousDeps[hookCallIdx][i]);

            if (isChanged) {
                memoData[hookCallIdx] = func();
                previousDeps[hookCallIdx] = deps;
            }

            return memoData[hookCallIdx];
        };

        if (
            condition({
                dataset: ds,
                row: i,
                rowData: $ds.getRowData(ds, i),
                useMemo,
            })
        ) {
            ds.deleteRow(i);
        }

        if (initialHookCallIdx !== -1) {
            initialHookCallIdx = hookCallIdx;
        }

        if (initialHookCallIdx !== -1 && initialHookCallIdx < hookCallIdx) {
            throw new HookOrderMismatchError();
        }
    }

    if (isSkipEvent) {
        ds.enableevent = defaultEnableEvent;
    }
    return ds;
}

/**
 * 인수로 받은 데이터셋에서 조건을 검사하여 통과한 행만 남김
 *
 * @function filterRowsByCondition
 * @param {nexacro.Dataset} dataset
 * 대상 데이터셋
 * @param {RemoveRowsByCondtionFunc} condition
 * 검사 조건
 * @param {RemoveRowsByCondtionOptions} [options]
 * @returns {nexacro.Dataset | null}
 * 행 삭제 한 데이터셋
 * @example
 * $f.filterRowsByCondition(this.ds_main, (context) => {
 *     return context.row % 2;
 * });
 *
 * @memberOf $f
 */
export function filterRowsByCondition(dataset, condition, options) {
    return removeRowsByCondition(
        dataset,
        typeof condition === "function" ? (e) => !condition(e) : null,
        options,
    );
}

export class HookOrderMismatchError extends Error {
    constructor() {
        super("호출 순서가 일치 하지 않습니다. 제어문에서 사용 불가합니다.");
    }
}