Source: object.mjs

/**
 * @module kongUtilObject
 */
import utilObject from "./core.mjs";

export * from "./core.mjs";

/**
 * @func emulateArray
 * @desc Emulate functions of Array class.
 * @param {string} method - name of the corresponding method of Array class.
 * @param {Function} callback
 * @param {Object} [target=this] - useful to assign this to prototype, though not recommended.
 * @returns {*} depends on `method`
 *
 * @example /// returns [4, 9]
    emulateArray("map", x => x*x, {a: 2, b: 3});
 *
 */
export function emulateArray(method, callback, target = this) {
    const keys = Object.keys(target);
    return keys[method](key => callback(target[key], key, target));
}


/**
 * @func objectMap
 * @param {Function} callback
 * @param {Object} [target=this]
 * @returns {Object.<*>}
 */
export function objectMap(callback, target = this) {
    const r = {};
    Object.keys(target).forEach(key => {
        r[key] = callback(target[key], key, target);
    });
    return r;
}

/**
 * @func objectMapAsync
 * @param {Function} callback
 * @param {Object} [target=this]
 * @param {boolean} [skipReturn=false]
 * @returns {Promise<Object|undefined>}
 */
export async function objectMapAsync(callback, target = this, skipReturn = false) {
    const results = {};
    const keys = Object.keys(target);
    for (let i = 0; i < keys.length; ++i) {
        const key = keys[i];
        const r = await callback(target[key], key, target);
        if (! skipReturn) results[key] = r;
    }
    if (! skipReturn) return results;
}


/**
 * @func objectReduce
 * @desc Emulate `Array.prototype.reduce`.
 * @param {Function} callback
 * @param {*} initial
 * @param {Object} [target=this]
 * @returns {*} depends on `callback`
 */
export function objectReduce(callback, initial, target = this) {
    const keys = Object.keys(target);
    return keys.reduce(
        (acc, key) => callback(acc, target[key], key, target),
        initial
    );
}


/**
 * @func objectReduceAsync
 * @desc Emulate `Array.prototype.reduce` in async way.
 * @param {Function} callback
 * @param {*} initial
 * @param {Object} [target=this]
 * @returns {*} depends on `callback`
 */
export async function objectReduceAsync(callback, initial, target = this) {
    let acc = await initial;
    const keys = Object.keys(target);
    for (let i = 0; i < keys.length; ++i) {
        const key = keys[i];
        acc = await callback(acc, target[key], key, target);
    }
    return acc;
}


/**
 * @func objectFlat
 * @desc Flatten an object
 * @param {Object} obj
 * @param {Integer} [depth=Infinity]
 * @param {string} [separator='.']
 * @param {string} [prefix='']
 * @returns {Object}
 *
 * @example /// returns {x.y: 3, x.q.a: true, x.q.b: null, z: 7}
    objectFlat({x: {y: 3, q: {a: true, b: null}}, z: 7});
 */
export function objectFlat(obj, depth = Infinity, separator = '.', prefix = '') {
    const result = {};
    for (let k in obj) {
        if (depth && obj[k]?.constructor === Object)
            Object.assign(result, objectFlat(obj[k], depth - 1, separator, prefix + k + separator));
        else result[prefix + k] = obj[k];
    }
    return result;
}


Object.assign(utilObject, {
    emulateArray,
    objectMap, objectMapAsync,
    objectReduce, objectReduceAsync,
    objectFlat
});

export default utilObject;