All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.dist.min-dash.js Maven / Gradle / Ivy

The newest version!
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MinDash = {}));
})(this, (function (exports) { 'use strict';

  /**
   * Flatten array, one level deep.
   *
   * @template T
   *
   * @param {T[][] | T[] | null} [arr]
   *
   * @return {T[]}
   */
  function flatten(arr) {
    return Array.prototype.concat.apply([], arr);
  }

  const nativeToString = Object.prototype.toString;
  const nativeHasOwnProperty = Object.prototype.hasOwnProperty;

  function isUndefined(obj) {
    return obj === undefined;
  }

  function isDefined(obj) {
    return obj !== undefined;
  }

  function isNil(obj) {
    return obj == null;
  }

  function isArray(obj) {
    return nativeToString.call(obj) === '[object Array]';
  }

  function isObject(obj) {
    return nativeToString.call(obj) === '[object Object]';
  }

  function isNumber(obj) {
    return nativeToString.call(obj) === '[object Number]';
  }

  /**
   * @param {any} obj
   *
   * @return {boolean}
   */
  function isFunction(obj) {
    const tag = nativeToString.call(obj);

    return (
      tag === '[object Function]' ||
      tag === '[object AsyncFunction]' ||
      tag === '[object GeneratorFunction]' ||
      tag === '[object AsyncGeneratorFunction]' ||
      tag === '[object Proxy]'
    );
  }

  function isString(obj) {
    return nativeToString.call(obj) === '[object String]';
  }


  /**
   * Ensure collection is an array.
   *
   * @param {Object} obj
   */
  function ensureArray(obj) {

    if (isArray(obj)) {
      return;
    }

    throw new Error('must supply array');
  }

  /**
   * Return true, if target owns a property with the given key.
   *
   * @param {Object} target
   * @param {String} key
   *
   * @return {Boolean}
   */
  function has(target, key) {
    return nativeHasOwnProperty.call(target, key);
  }

  /**
   * @template T
   * @typedef { (
   *   ((e: T) => boolean) |
   *   ((e: T, idx: number) => boolean) |
   *   ((e: T, key: string) => boolean) |
   *   string |
   *   number
   * ) } Matcher
   */

  /**
   * @template T
   * @template U
   *
   * @typedef { (
   *   ((e: T) => U) | string | number
   * ) } Extractor
   */


  /**
   * @template T
   * @typedef { (val: T, key: any) => boolean } MatchFn
   */

  /**
   * @template T
   * @typedef { T[] } ArrayCollection
   */

  /**
   * @template T
   * @typedef { { [key: string]: T } } StringKeyValueCollection
   */

  /**
   * @template T
   * @typedef { { [key: number]: T } } NumberKeyValueCollection
   */

  /**
   * @template T
   * @typedef { StringKeyValueCollection | NumberKeyValueCollection } KeyValueCollection
   */

  /**
   * @template T
   * @typedef { KeyValueCollection | ArrayCollection } Collection
   */

  /**
   * Find element in collection.
   *
   * @template T
   * @param {Collection} collection
   * @param {Matcher} matcher
   *
   * @return {Object}
   */
  function find(collection, matcher) {

    const matchFn = toMatcher(matcher);

    let match;

    forEach(collection, function(val, key) {
      if (matchFn(val, key)) {
        match = val;

        return false;
      }
    });

    return match;

  }


  /**
   * Find element index in collection.
   *
   * @template T
   * @param {Collection} collection
   * @param {Matcher} matcher
   *
   * @return {number}
   */
  function findIndex(collection, matcher) {

    const matchFn = toMatcher(matcher);

    let idx = isArray(collection) ? -1 : undefined;

    forEach(collection, function(val, key) {
      if (matchFn(val, key)) {
        idx = key;

        return false;
      }
    });

    return idx;
  }


  /**
   * Filter elements in collection.
   *
   * @template T
   * @param {Collection} collection
   * @param {Matcher} matcher
   *
   * @return {T[]} result
   */
  function filter(collection, matcher) {

    const matchFn = toMatcher(matcher);

    let result = [];

    forEach(collection, function(val, key) {
      if (matchFn(val, key)) {
        result.push(val);
      }
    });

    return result;
  }


  /**
   * Iterate over collection; returning something
   * (non-undefined) will stop iteration.
   *
   * @template T
   * @param {Collection} collection
   * @param { ((item: T, idx: number) => (boolean|void)) | ((item: T, key: string) => (boolean|void)) } iterator
   *
   * @return {T} return result that stopped the iteration
   */
  function forEach(collection, iterator) {

    let val,
        result;

    if (isUndefined(collection)) {
      return;
    }

    const convertKey = isArray(collection) ? toNum : identity;

    for (let key in collection) {

      if (has(collection, key)) {
        val = collection[key];

        result = iterator(val, convertKey(key));

        if (result === false) {
          return val;
        }
      }
    }
  }

  /**
   * Return collection without element.
   *
   * @template T
   * @param {ArrayCollection} arr
   * @param {Matcher} matcher
   *
   * @return {T[]}
   */
  function without(arr, matcher) {

    if (isUndefined(arr)) {
      return [];
    }

    ensureArray(arr);

    const matchFn = toMatcher(matcher);

    return arr.filter(function(el, idx) {
      return !matchFn(el, idx);
    });

  }


  /**
   * Reduce collection, returning a single result.
   *
   * @template T
   * @template V
   *
   * @param {Collection} collection
   * @param {(result: V, entry: T, index: any) => V} iterator
   * @param {V} result
   *
   * @return {V} result returned from last iterator
   */
  function reduce(collection, iterator, result) {

    forEach(collection, function(value, idx) {
      result = iterator(result, value, idx);
    });

    return result;
  }


  /**
   * Return true if every element in the collection
   * matches the criteria.
   *
   * @param  {Object|Array} collection
   * @param  {Function} matcher
   *
   * @return {Boolean}
   */
  function every(collection, matcher) {

    return !!reduce(collection, function(matches, val, key) {
      return matches && matcher(val, key);
    }, true);
  }


  /**
   * Return true if some elements in the collection
   * match the criteria.
   *
   * @param  {Object|Array} collection
   * @param  {Function} matcher
   *
   * @return {Boolean}
   */
  function some(collection, matcher) {

    return !!find(collection, matcher);
  }


  /**
   * Transform a collection into another collection
   * by piping each member through the given fn.
   *
   * @param  {Object|Array}   collection
   * @param  {Function} fn
   *
   * @return {Array} transformed collection
   */
  function map(collection, fn) {

    let result = [];

    forEach(collection, function(val, key) {
      result.push(fn(val, key));
    });

    return result;
  }


  /**
   * Get the collections keys.
   *
   * @param  {Object|Array} collection
   *
   * @return {Array}
   */
  function keys(collection) {
    return collection && Object.keys(collection) || [];
  }


  /**
   * Shorthand for `keys(o).length`.
   *
   * @param  {Object|Array} collection
   *
   * @return {Number}
   */
  function size(collection) {
    return keys(collection).length;
  }


  /**
   * Get the values in the collection.
   *
   * @param  {Object|Array} collection
   *
   * @return {Array}
   */
  function values(collection) {
    return map(collection, (val) => val);
  }


  /**
   * Group collection members by attribute.
   *
   * @param {Object|Array} collection
   * @param {Extractor} extractor
   *
   * @return {Object} map with { attrValue => [ a, b, c ] }
   */
  function groupBy(collection, extractor, grouped = {}) {

    extractor = toExtractor(extractor);

    forEach(collection, function(val) {
      let discriminator = extractor(val) || '_';

      let group = grouped[discriminator];

      if (!group) {
        group = grouped[discriminator] = [];
      }

      group.push(val);
    });

    return grouped;
  }


  function uniqueBy(extractor, ...collections) {

    extractor = toExtractor(extractor);

    let grouped = {};

    forEach(collections, (c) => groupBy(c, extractor, grouped));

    let result = map(grouped, function(val, key) {
      return val[0];
    });

    return result;
  }


  const unionBy = uniqueBy;



  /**
   * Sort collection by criteria.
   *
   * @template T
   *
   * @param {Collection} collection
   * @param {Extractor} extractor
   *
   * @return {Array}
   */
  function sortBy(collection, extractor) {

    extractor = toExtractor(extractor);

    let sorted = [];

    forEach(collection, function(value, key) {
      let disc = extractor(value, key);

      let entry = {
        d: disc,
        v: value
      };

      for (var idx = 0; idx < sorted.length; idx++) {
        let { d } = sorted[idx];

        if (disc < d) {
          sorted.splice(idx, 0, entry);
          return;
        }
      }

      // not inserted, append (!)
      sorted.push(entry);
    });

    return map(sorted, (e) => e.v);
  }


  /**
   * Create an object pattern matcher.
   *
   * @example
   *
   * ```javascript
   * const matcher = matchPattern({ id: 1 });
   *
   * let element = find(elements, matcher);
   * ```
   *
   * @template T
   *
   * @param {T} pattern
   *
   * @return { (el: any) =>  boolean } matcherFn
   */
  function matchPattern(pattern) {

    return function(el) {

      return every(pattern, function(val, key) {
        return el[key] === val;
      });

    };
  }


  /**
   * @param {string | ((e: any) => any) } extractor
   *
   * @return { (e: any) => any }
   */
  function toExtractor(extractor) {

    /**
     * @satisfies { (e: any) => any }
     */
    return isFunction(extractor) ? extractor : (e) => {

      // @ts-ignore: just works
      return e[extractor];
    };
  }


  /**
   * @template T
   * @param {Matcher} matcher
   *
   * @return {MatchFn}
   */
  function toMatcher(matcher) {
    return isFunction(matcher) ? matcher : (e) => {
      return e === matcher;
    };
  }


  function identity(arg) {
    return arg;
  }

  function toNum(arg) {
    return Number(arg);
  }

  /* global setTimeout clearTimeout */

  /**
   * @typedef { {
   *   (...args: any[]): any;
   *   flush: () => void;
   *   cancel: () => void;
   * } } DebouncedFunction
   */

  /**
   * Debounce fn, calling it only once if the given time
   * elapsed between calls.
   *
   * Lodash-style the function exposes methods to `#clear`
   * and `#flush` to control internal behavior.
   *
   * @param  {Function} fn
   * @param  {Number} timeout
   *
   * @return {DebouncedFunction} debounced function
   */
  function debounce(fn, timeout) {

    let timer;

    let lastArgs;
    let lastThis;

    let lastNow;

    function fire(force) {

      let now = Date.now();

      let scheduledDiff = force ? 0 : (lastNow + timeout) - now;

      if (scheduledDiff > 0) {
        return schedule(scheduledDiff);
      }

      fn.apply(lastThis, lastArgs);

      clear();
    }

    function schedule(timeout) {
      timer = setTimeout(fire, timeout);
    }

    function clear() {
      if (timer) {
        clearTimeout(timer);
      }

      timer = lastNow = lastArgs = lastThis = undefined;
    }

    function flush() {
      if (timer) {
        fire(true);
      }

      clear();
    }

    /**
     * @type { DebouncedFunction }
     */
    function callback(...args) {
      lastNow = Date.now();

      lastArgs = args;
      lastThis = this;

      // ensure an execution is scheduled
      if (!timer) {
        schedule(timeout);
      }
    }

    callback.flush = flush;
    callback.cancel = clear;

    return callback;
  }

  /**
   * Throttle fn, calling at most once
   * in the given interval.
   *
   * @param  {Function} fn
   * @param  {Number} interval
   *
   * @return {Function} throttled function
   */
  function throttle(fn, interval) {
    let throttling = false;

    return function(...args) {

      if (throttling) {
        return;
      }

      fn(...args);
      throttling = true;

      setTimeout(() => {
        throttling = false;
      }, interval);
    };
  }

  /**
   * Bind function against target .
   *
   * @param  {Function} fn
   * @param  {Object}   target
   *
   * @return {Function} bound function
   */
  function bind(fn, target) {
    return fn.bind(target);
  }

  /**
   * Convenience wrapper for `Object.assign`.
   *
   * @param {Object} target
   * @param {...Object} others
   *
   * @return {Object} the target
   */
  function assign(target, ...others) {
    return Object.assign(target, ...others);
  }

  /**
   * Sets a nested property of a given object to the specified value.
   *
   * This mutates the object and returns it.
   *
   * @template T
   *
   * @param {T} target The target of the set operation.
   * @param {(string|number)[]} path The path to the nested value.
   * @param {any} value The value to set.
   *
   * @return {T}
   */
  function set(target, path, value) {

    let currentTarget = target;

    forEach(path, function(key, idx) {

      if (typeof key !== 'number' && typeof key !== 'string') {
        throw new Error('illegal key type: ' + typeof key + '. Key should be of type number or string.');
      }

      if (key === 'constructor') {
        throw new Error('illegal key: constructor');
      }

      if (key === '__proto__') {
        throw new Error('illegal key: __proto__');
      }

      let nextKey = path[idx + 1];
      let nextTarget = currentTarget[key];

      if (isDefined(nextKey) && isNil(nextTarget)) {
        nextTarget = currentTarget[key] = isNaN(+nextKey) ? {} : [];
      }

      if (isUndefined(nextKey)) {
        if (isUndefined(value)) {
          delete currentTarget[key];
        } else {
          currentTarget[key] = value;
        }
      } else {
        currentTarget = nextTarget;
      }
    });

    return target;
  }


  /**
   * Gets a nested property of a given object.
   *
   * @param {Object} target The target of the get operation.
   * @param {(string|number)[]} path The path to the nested value.
   * @param {any} [defaultValue] The value to return if no value exists.
   *
   * @return {any}
   */
  function get(target, path, defaultValue) {

    let currentTarget = target;

    forEach(path, function(key) {

      // accessing nil property yields 
      if (isNil(currentTarget)) {
        currentTarget = undefined;

        return false;
      }

      currentTarget = currentTarget[key];
    });

    return isUndefined(currentTarget) ? defaultValue : currentTarget;
  }

  /**
   * Pick properties from the given target.
   *
   * @template T
   * @template {any[]} V
   *
   * @param {T} target
   * @param {V} properties
   *
   * @return Pick
   */
  function pick(target, properties) {

    let result = {};

    let obj = Object(target);

    forEach(properties, function(prop) {

      if (prop in obj) {
        result[prop] = target[prop];
      }
    });

    return result;
  }

  /**
   * Pick all target properties, excluding the given ones.
   *
   * @template T
   * @template {any[]} V
   *
   * @param {T} target
   * @param {V} properties
   *
   * @return {Omit} target
   */
  function omit(target, properties) {

    let result = {};

    let obj = Object(target);

    forEach(obj, function(prop, key) {

      if (properties.indexOf(key) === -1) {
        result[key] = prop;
      }
    });

    return result;
  }

  /**
   * Recursively merge `...sources` into given target.
   *
   * Does support merging objects; does not support merging arrays.
   *
   * @param {Object} target
   * @param {...Object} sources
   *
   * @return {Object} the target
   */
  function merge(target, ...sources) {

    if (!sources.length) {
      return target;
    }

    forEach(sources, function(source) {

      // skip non-obj sources, i.e. null
      if (!source || !isObject(source)) {
        return;
      }

      forEach(source, function(sourceVal, key) {

        if (key === '__proto__') {
          return;
        }

        let targetVal = target[key];

        if (isObject(sourceVal)) {

          if (!isObject(targetVal)) {

            // override target[key] with object
            targetVal = {};
          }

          target[key] = merge(targetVal, sourceVal);
        } else {
          target[key] = sourceVal;
        }

      });
    });

    return target;
  }

  exports.assign = assign;
  exports.bind = bind;
  exports.debounce = debounce;
  exports.ensureArray = ensureArray;
  exports.every = every;
  exports.filter = filter;
  exports.find = find;
  exports.findIndex = findIndex;
  exports.flatten = flatten;
  exports.forEach = forEach;
  exports.get = get;
  exports.groupBy = groupBy;
  exports.has = has;
  exports.isArray = isArray;
  exports.isDefined = isDefined;
  exports.isFunction = isFunction;
  exports.isNil = isNil;
  exports.isNumber = isNumber;
  exports.isObject = isObject;
  exports.isString = isString;
  exports.isUndefined = isUndefined;
  exports.keys = keys;
  exports.map = map;
  exports.matchPattern = matchPattern;
  exports.merge = merge;
  exports.omit = omit;
  exports.pick = pick;
  exports.reduce = reduce;
  exports.set = set;
  exports.size = size;
  exports.some = some;
  exports.sortBy = sortBy;
  exports.throttle = throttle;
  exports.unionBy = unionBy;
  exports.uniqueBy = uniqueBy;
  exports.values = values;
  exports.without = without;

}));




© 2015 - 2024 Weber Informatics LLC | Privacy Policy