forked from kevin.shehu/GGD
420 lines
12 KiB
JavaScript
420 lines
12 KiB
JavaScript
/*
|
|
Utils using lodash style API. (not necessarily 100% compliant) for functional and other utils.
|
|
These utils should replace usage of lodash in the production code base. not because they are any better...
|
|
but for the purpose of being a dependency free library.
|
|
|
|
The hotspots in the code are already written in imperative style for performance reasons.
|
|
so writing several dozen utils which may be slower than the original lodash, does not matter as much
|
|
considering they will not be invoked in hotspots...
|
|
*/
|
|
export function isEmpty(arr) {
|
|
return arr && arr.length === 0;
|
|
}
|
|
export function keys(obj) {
|
|
if (obj === undefined || obj === null) {
|
|
return [];
|
|
}
|
|
return Object.keys(obj);
|
|
}
|
|
export function values(obj) {
|
|
var vals = [];
|
|
var keys = Object.keys(obj);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
vals.push(obj[keys[i]]);
|
|
}
|
|
return vals;
|
|
}
|
|
export function mapValues(obj, callback) {
|
|
var result = [];
|
|
var objKeys = keys(obj);
|
|
for (var idx = 0; idx < objKeys.length; idx++) {
|
|
var currKey = objKeys[idx];
|
|
result.push(callback.call(null, obj[currKey], currKey));
|
|
}
|
|
return result;
|
|
}
|
|
export function map(arr, callback) {
|
|
var result = [];
|
|
for (var idx = 0; idx < arr.length; idx++) {
|
|
result.push(callback.call(null, arr[idx], idx));
|
|
}
|
|
return result;
|
|
}
|
|
export function flatten(arr) {
|
|
var result = [];
|
|
for (var idx = 0; idx < arr.length; idx++) {
|
|
var currItem = arr[idx];
|
|
if (Array.isArray(currItem)) {
|
|
result = result.concat(flatten(currItem));
|
|
}
|
|
else {
|
|
result.push(currItem);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
export function first(arr) {
|
|
return isEmpty(arr) ? undefined : arr[0];
|
|
}
|
|
export function last(arr) {
|
|
var len = arr && arr.length;
|
|
return len ? arr[len - 1] : undefined;
|
|
}
|
|
export function forEach(collection, iteratorCallback) {
|
|
/* istanbul ignore else */
|
|
if (Array.isArray(collection)) {
|
|
for (var i = 0; i < collection.length; i++) {
|
|
iteratorCallback.call(null, collection[i], i);
|
|
}
|
|
}
|
|
else if (isObject(collection)) {
|
|
var colKeys = keys(collection);
|
|
for (var i = 0; i < colKeys.length; i++) {
|
|
var key = colKeys[i];
|
|
var value = collection[key];
|
|
iteratorCallback.call(null, value, key);
|
|
}
|
|
}
|
|
else {
|
|
throw Error("non exhaustive match");
|
|
}
|
|
}
|
|
export function isString(item) {
|
|
return typeof item === "string";
|
|
}
|
|
export function isUndefined(item) {
|
|
return item === undefined;
|
|
}
|
|
export function isFunction(item) {
|
|
return item instanceof Function;
|
|
}
|
|
export function drop(arr, howMuch) {
|
|
if (howMuch === void 0) { howMuch = 1; }
|
|
return arr.slice(howMuch, arr.length);
|
|
}
|
|
export function dropRight(arr, howMuch) {
|
|
if (howMuch === void 0) { howMuch = 1; }
|
|
return arr.slice(0, arr.length - howMuch);
|
|
}
|
|
export function filter(arr, predicate) {
|
|
var result = [];
|
|
if (Array.isArray(arr)) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
var item = arr[i];
|
|
if (predicate.call(null, item)) {
|
|
result.push(item);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
export function reject(arr, predicate) {
|
|
return filter(arr, function (item) { return !predicate(item); });
|
|
}
|
|
export function pick(obj, predicate) {
|
|
var keys = Object.keys(obj);
|
|
var result = {};
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var currKey = keys[i];
|
|
var currItem = obj[currKey];
|
|
if (predicate(currItem)) {
|
|
result[currKey] = currItem;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
export function has(obj, prop) {
|
|
if (isObject(obj)) {
|
|
return obj.hasOwnProperty(prop);
|
|
}
|
|
return false;
|
|
}
|
|
export function contains(arr, item) {
|
|
return find(arr, function (currItem) { return currItem === item; }) !== undefined ? true : false;
|
|
}
|
|
/**
|
|
* shallow clone
|
|
*/
|
|
export function cloneArr(arr) {
|
|
var newArr = [];
|
|
for (var i = 0; i < arr.length; i++) {
|
|
newArr.push(arr[i]);
|
|
}
|
|
return newArr;
|
|
}
|
|
/**
|
|
* shallow clone
|
|
*/
|
|
export function cloneObj(obj) {
|
|
var clonedObj = {};
|
|
for (var key in obj) {
|
|
/* istanbul ignore else */
|
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
clonedObj[key] = obj[key];
|
|
}
|
|
}
|
|
return clonedObj;
|
|
}
|
|
export function find(arr, predicate) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
var item = arr[i];
|
|
if (predicate.call(null, item)) {
|
|
return item;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
export function findAll(arr, predicate) {
|
|
var found = [];
|
|
for (var i = 0; i < arr.length; i++) {
|
|
var item = arr[i];
|
|
if (predicate.call(null, item)) {
|
|
found.push(item);
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
export function reduce(arrOrObj, iterator, initial) {
|
|
var isArr = Array.isArray(arrOrObj);
|
|
var vals = isArr ? arrOrObj : values(arrOrObj);
|
|
var objKeys = isArr ? [] : keys(arrOrObj);
|
|
var accumulator = initial;
|
|
for (var i = 0; i < vals.length; i++) {
|
|
accumulator = iterator.call(null, accumulator, vals[i], isArr ? i : objKeys[i]);
|
|
}
|
|
return accumulator;
|
|
}
|
|
export function compact(arr) {
|
|
return reject(arr, function (item) { return item === null || item === undefined; });
|
|
}
|
|
export function uniq(arr, identity) {
|
|
if (identity === void 0) { identity = function (item) { return item; }; }
|
|
var identities = [];
|
|
return reduce(arr, function (result, currItem) {
|
|
var currIdentity = identity(currItem);
|
|
if (contains(identities, currIdentity)) {
|
|
return result;
|
|
}
|
|
else {
|
|
identities.push(currIdentity);
|
|
return result.concat(currItem);
|
|
}
|
|
}, []);
|
|
}
|
|
export function partial(func) {
|
|
var restArgs = [];
|
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
restArgs[_i - 1] = arguments[_i];
|
|
}
|
|
var firstArg = [null];
|
|
var allArgs = firstArg.concat(restArgs);
|
|
return Function.bind.apply(func, allArgs);
|
|
}
|
|
export function isArray(obj) {
|
|
return Array.isArray(obj);
|
|
}
|
|
export function isRegExp(obj) {
|
|
return obj instanceof RegExp;
|
|
}
|
|
export function isObject(obj) {
|
|
return obj instanceof Object;
|
|
}
|
|
export function every(arr, predicate) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
if (!predicate(arr[i], i)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
export function difference(arr, values) {
|
|
return reject(arr, function (item) { return contains(values, item); });
|
|
}
|
|
export function some(arr, predicate) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
if (predicate(arr[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
export function indexOf(arr, value) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
if (arr[i] === value) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
export function sortBy(arr, orderFunc) {
|
|
var result = cloneArr(arr);
|
|
result.sort(function (a, b) { return orderFunc(a) - orderFunc(b); });
|
|
return result;
|
|
}
|
|
export function zipObject(keys, values) {
|
|
if (keys.length !== values.length) {
|
|
throw Error("can't zipObject with different number of keys and values!");
|
|
}
|
|
var result = {};
|
|
for (var i = 0; i < keys.length; i++) {
|
|
result[keys[i]] = values[i];
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* mutates! (and returns) target
|
|
*/
|
|
export function assign(target) {
|
|
var sources = [];
|
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
sources[_i - 1] = arguments[_i];
|
|
}
|
|
for (var i = 0; i < sources.length; i++) {
|
|
var curSource = sources[i];
|
|
var currSourceKeys = keys(curSource);
|
|
for (var j = 0; j < currSourceKeys.length; j++) {
|
|
var currKey = currSourceKeys[j];
|
|
target[currKey] = curSource[currKey];
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
/**
|
|
* mutates! (and returns) target
|
|
*/
|
|
export function assignNoOverwrite(target) {
|
|
var sources = [];
|
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
sources[_i - 1] = arguments[_i];
|
|
}
|
|
for (var i = 0; i < sources.length; i++) {
|
|
var curSource = sources[i];
|
|
if (isUndefined(curSource)) {
|
|
continue;
|
|
}
|
|
var currSourceKeys = keys(curSource);
|
|
for (var j = 0; j < currSourceKeys.length; j++) {
|
|
var currKey = currSourceKeys[j];
|
|
if (!has(target, currKey)) {
|
|
target[currKey] = curSource[currKey];
|
|
}
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
export function defaults() {
|
|
var sources = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
sources[_i] = arguments[_i];
|
|
}
|
|
return assignNoOverwrite.apply(null, [{}].concat(sources));
|
|
}
|
|
export function groupBy(arr, groupKeyFunc) {
|
|
var result = {};
|
|
forEach(arr, function (item) {
|
|
var currGroupKey = groupKeyFunc(item);
|
|
var currGroupArr = result[currGroupKey];
|
|
if (currGroupArr) {
|
|
currGroupArr.push(item);
|
|
}
|
|
else {
|
|
result[currGroupKey] = [item];
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
/**
|
|
* Merge obj2 into obj1.
|
|
* Will overwrite existing properties with the same name
|
|
*/
|
|
export function merge(obj1, obj2) {
|
|
var result = cloneObj(obj1);
|
|
var keys2 = keys(obj2);
|
|
for (var i = 0; i < keys2.length; i++) {
|
|
var key = keys2[i];
|
|
var value = obj2[key];
|
|
result[key] = value;
|
|
}
|
|
return result;
|
|
}
|
|
export function NOOP() { }
|
|
export function IDENTITY(item) {
|
|
return item;
|
|
}
|
|
/**
|
|
* Will return a new packed array with same values.
|
|
*/
|
|
export function packArray(holeyArr) {
|
|
var result = [];
|
|
for (var i = 0; i < holeyArr.length; i++) {
|
|
var orgValue = holeyArr[i];
|
|
result.push(orgValue !== undefined ? orgValue : undefined);
|
|
}
|
|
return result;
|
|
}
|
|
export function PRINT_ERROR(msg) {
|
|
/* istanbul ignore else - can't override global.console in node.js */
|
|
if (console && console.error) {
|
|
console.error("Error: " + msg);
|
|
}
|
|
}
|
|
export function PRINT_WARNING(msg) {
|
|
/* istanbul ignore else - can't override global.console in node.js*/
|
|
if (console && console.warn) {
|
|
// TODO: modify docs accordingly
|
|
console.warn("Warning: " + msg);
|
|
}
|
|
}
|
|
export function isES2015MapSupported() {
|
|
return typeof Map === "function";
|
|
}
|
|
export function applyMixins(derivedCtor, baseCtors) {
|
|
baseCtors.forEach(function (baseCtor) {
|
|
var baseProto = baseCtor.prototype;
|
|
Object.getOwnPropertyNames(baseProto).forEach(function (propName) {
|
|
if (propName === "constructor") {
|
|
return;
|
|
}
|
|
var basePropDescriptor = Object.getOwnPropertyDescriptor(baseProto, propName);
|
|
// Handle Accessors
|
|
if (basePropDescriptor &&
|
|
(basePropDescriptor.get || basePropDescriptor.set)) {
|
|
Object.defineProperty(derivedCtor.prototype, propName, basePropDescriptor);
|
|
}
|
|
else {
|
|
derivedCtor.prototype[propName] = baseCtor.prototype[propName];
|
|
}
|
|
});
|
|
});
|
|
}
|
|
// base on: https://github.com/petkaantonov/bluebird/blob/b97c0d2d487e8c5076e8bd897e0dcd4622d31846/src/util.js#L201-L216
|
|
export function toFastProperties(toBecomeFast) {
|
|
function FakeConstructor() { }
|
|
// If our object is used as a constructor it would receive
|
|
FakeConstructor.prototype = toBecomeFast;
|
|
var fakeInstance = new FakeConstructor();
|
|
function fakeAccess() {
|
|
return typeof fakeInstance.bar;
|
|
}
|
|
// help V8 understand this is a "real" prototype by actually using
|
|
// the fake instance.
|
|
fakeAccess();
|
|
fakeAccess();
|
|
return toBecomeFast;
|
|
// Eval prevents optimization of this method (even though this is dead code)
|
|
/* istanbul ignore next */
|
|
// tslint:disable-next-line
|
|
eval(toBecomeFast);
|
|
}
|
|
export function peek(arr) {
|
|
return arr[arr.length - 1];
|
|
}
|
|
/* istanbul ignore next - for performance tracing*/
|
|
export function timer(func) {
|
|
var start = new Date().getTime();
|
|
var val = func();
|
|
var end = new Date().getTime();
|
|
var total = end - start;
|
|
return { time: total, value: val };
|
|
}
|
|
//# sourceMappingURL=utils.js.map
|