Source: utils/reducer-store.js

import { defReducer, fnList } from '../operator';

/**
 * A page level storage which stores, registers, unregisters reducers for all the datamodel instances. There is only one
 * reducer store available in a page. All the datamodel instances receive same instance of reducer store. DataModel
 * out of the box provides handful of {@link reducer | reducers} which can be used as reducer function.
 *
 * @public
 * @namespace DataModel
 */
class ReducerStore {
    constructor () {
        this.store = new Map();
        this.store.set('defReducer', defReducer);

        Object.entries(fnList).forEach((key) => {
            this.store.set(key[0], key[1]);
        });
    }

    /**
     * Changes the `defaultReducer` globally. For all the fields which does not have `defAggFn` mentioned in schema, the
     * value of `defaultReducer` is used for aggregation.
     *
     * @public
     *
     * @param {string} [reducer='sum'] name of the default reducer. It picks up the definition from store by doing name
     *      lookup. If no name is found then it takes `sum` as the default reducer.
     *
     * @return {ReducerStore} instance of the singleton store in page.
     */
    defaultReducer (...params) {
        if (params.length) {
            let reducer = params[0];
            if (typeof reducer === 'function') {
                this.store.set('defReducer', reducer);
            } else if (typeof reducer === 'string') {
                if (Object.keys(fnList).indexOf(reducer) !== -1) {
                    this.store.set('defReducer', fnList[reducer]);
                }
            }
            return this;
        }

        return this.store.get('defReducer');
    }

    /**
     *
     * Registers a {@link reducer | reducer}.
     * A {@link reducer | reducer} has to be registered before it is used.
     *
     * @example
     *  //@preamble_start
     *  Promise.all([loadData('/static/cars.json'), loadData('/static/cars-schema.json')]).then(function (params) {
     *      const data = params[0];
     *      const schema = params[1];
     *      const DataModel = muze.DataModel;
     *      const dm = new DataModel(data, schema);
     *  //@preamble_end
     *  // DataModel instance is created from https://www.charts.com/static/cars.json data,
     *  // https://www.charts.com/static/cars-schema.json schema and assigned to variable dm. DataModel is retrieved
     *  // from muze namespace and assigned to DataModel variable.
     *  const reducerStore = DataModel.Reducers();
     *
     *  reducerStore.register('meanSquared', (arr) => {
     *      const squaredVal = arr.map(item => item * item);
     *      let sum = 0;
     *      for (let i = 0, l = squaredVal.length; i < l; i++) {
     *          sum += squaredVal[i];
     *      }
     *      return sum;
     *  });
     *
     *  const outputDM= dm.groupBy(['origin'], {
     *      accleration: 'meanSquared'
     *  });
     *  //@preamble_start
     *  printDM(outputDM);
     *  });
     *  //@preamble_end
     *
     * @public
     *
     * @param {string} name formal name for a reducer. If the given name already exists in store it is overridden by new
     *      definition.
     * @param {Function} reducer definition of {@link reducer} function.
     *
     * @return {Function} function for unregistering the reducer.
     */
    register (name, reducer) {
        if (typeof name === 'string' && typeof reducer === 'function') {
            this.store.set(name, reducer);
        }

        return () => { this.__unregister(name); };
    }

    __unregister (name) {
        if (this.store.has(name)) {
            this.store.delete(name);
        }
    }

    resolve (name) {
        if (name instanceof Function) {
            return name;
        }
        return this.store.get(name);
    }
}

const reducerStore = (function () {
    let store = null;

    function getStore () {
        if (store === null) {
            store = new ReducerStore();
        }
        return store;
    }
    return getStore();
}());

export default reducerStore;