space-lift
Version:
TypeScript Array, Object, Map, Set, Union, Enum utils
283 lines (282 loc) • 8.62 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.range = exports.setArrayPipe = exports.ArrayWrapper = void 0;
const function_1 = require("./function");
const wrapper_1 = require("./wrapper");
const map_1 = require("./map");
/** An Array wrapper providing extra functionalities and more chaining opportunities */
class ArrayWrapper {
constructor(_value) {
this._value = _value;
this._isLiftWrapper = true;
/**
* Pipes this Array with an arbitrary transformation function.
*/
this.pipe = pipe;
}
value() {
return this._value;
}
_clone() {
return this._value.slice();
}
/**
* Appends one item at the end of the Array.
*/
append(item) {
return new ArrayWrapper([...this._value, item]);
}
/**
* Appends an Iterable of items at the end of the Array.
*/
appendAll(items) {
return new ArrayWrapper([...this._value, ...items]);
}
/**
* Filters all the falsy elements out of this Array.
* All occurences of false, null, undefined, 0, "" will be removed.
*/
compact() {
return this.filter(x => !!x);
}
/**
* Counts the items satisfying a predicate.
*/
count(predicate) {
return this.pipe(arr => arr.reduce((count, item, index) => count + (predicate(item, index) ? 1 : 0), 0));
}
/**
* Maps this Array's items, unless void or undefined is returned, in which case the item is filtered.
* This is effectively a `filter` + `map` combined in one.
*/
collect(iterator) {
const result = [];
this._value.forEach((item, index) => {
const res = iterator(item, index);
if (res !== undefined)
result.push(res);
});
return new ArrayWrapper(result);
}
/**
* Creates an array without any duplicate item.
* If a key function is passed, items will be compared based on the result of that function;
* if not, they will be compared using strict equality.
*/
distinct(getKey) {
const items = new Set();
return this.collect((item, index) => {
const key = getKey ? getKey(item, index) : item;
if (items.has(key))
return;
items.add(key);
return item;
});
}
/**
* Drops the first 'count' items from this Array.
*/
drop(count) {
return this.pipe(arr => arr.slice(count));
}
/**
* Drops the last 'count' items from this Array.
*/
dropRight(count) {
return this.pipe(arr => arr.slice(0, -count));
}
filter(predicate) {
return this.pipe(arr => arr.filter(predicate));
}
/**
* Returns the first element in this Array or undefined.
*/
first() {
return this._value[0];
}
/**
* Maps this Array to an Array of Array | ArrayWrapper using a mapper function then flattens it.
*/
flatMap(fun) {
const arr = this._value, result = [];
for (let i = 0; i < arr.length; i++) {
result.push.apply(result, wrapper_1.getValue(fun(arr[i], i)));
}
return new ArrayWrapper(result);
}
flatten() {
const arr = this.value(), result = [];
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
result.push.apply(result, item);
}
return new ArrayWrapper(result);
}
/**
* Reduces this Array into a single value, using a starting value.
*/
reduce(startValue, func) {
return this.pipe(arr => arr.reduce(func, startValue));
}
/**
* Returns the item found at the provided index or undefined.
*/
get(index) {
return this._value[index];
}
/**
* Creates a Map where keys are the results of running each element through a discriminator function.
* The corresponding value of each key is an array of the elements responsible for generating the key.
*/
groupBy(discriminator) {
const groups = new Map();
this._value.forEach((item, index) => {
const key = discriminator(item, index);
if (!groups.has(key))
groups.set(key, []);
groups.get(key).push(item);
});
return new map_1.MapWrapper(groups);
}
/**
* Creates a new Array where each sub array contains at most 'bySize' elements.
*/
grouped(bySize) {
return this.pipe(arr => {
const result = [];
let current = [];
for (let i = 0; i < arr.length; i++) {
if (current.length < bySize) {
current.push(arr[i]);
}
if (current.length === bySize || i === arr.length - 1) {
result.push(current);
current = [];
}
}
return result;
});
}
/**
* Inserts an item at a specified index.
*/
insert(index, item) {
return this.pipe(arr => {
const result = arr.slice();
result.splice(index, 0, item);
return result;
});
}
/**
* Returns the item found at the last index or undefined.
*/
last() {
return this._value[this._value.length - 1];
}
/**
* Maps this Array using a mapper function.
*/
map(fun) {
return this.pipe(arr => arr.map(fun));
}
/**
* Removes the item found at the specified index.
*/
removeAt(index) {
const result = this._clone();
result.splice(index, index < 0 ? 0 : 1);
return new ArrayWrapper(result);
}
/**
* Reverses the Array.
*/
reverse() {
return this.pipe(arr => arr.slice().reverse());
}
/**
* Sorts the Array in ascending order, using one or more iterators specifying which field to compare.
* For strings, localCompare is used.
* The sort is stable if the browser uses a stable sort (all modern engines do)
*/
sort(...fields) {
const arr = this._clone();
if (fields.length === 0) {
fields = [function_1.identity];
}
arr.sort((a, b) => {
for (let i = 0, length = fields.length; i < length; i++) {
const fieldA = fields[i](a);
const fieldB = fields[i](b);
// Non defined values always go at the end
if (fieldA === null || fieldA === undefined)
return 1;
if (fieldB === null || fieldB === undefined)
return -1;
if (typeof fieldA === 'string') {
const comparison = fieldA.localeCompare(fieldB);
if (comparison !== 0)
return comparison;
}
else if (fieldA < fieldB)
return -1;
else if (fieldA > fieldB)
return 1;
}
return 0;
});
return new ArrayWrapper(arr);
}
/**
* Takes the first 'count' items from this Array.
*/
take(count) {
return this.pipe(_ => _.slice(0, count));
}
/**
* Takes the last 'count' items from this Array.
*/
takeRight(count) {
return this.pipe(arr => (arr.length < count ? arr.slice(0) : arr.slice(arr.length - count)));
}
/**
* Converts this Array to a Set.
*/
toSet() {
return this.pipe(arr => new Set(arr));
}
/**
* Updates an item at the specified index.
*/
updateAt(index, updater) {
const result = this._clone();
if (result.length > index && index > -1)
result[index] = wrapper_1.getValue(updater(result[index]));
return new ArrayWrapper(result);
}
}
exports.ArrayWrapper = ArrayWrapper;
let pipe;
function setArrayPipe(_pipe) {
pipe = _pipe;
}
exports.setArrayPipe = setArrayPipe;
/*
* Returns a number[] wrapper with all numbers from start to stop (inclusive),
* incremented or decremented by step.
*/
function range(start, stop, step) {
if (arguments.length === 1) {
stop = arguments[0] - 1;
start = 0;
}
step = step || 1;
const result = [];
const increasing = step > 0;
let next = start;
while ((increasing && next <= stop) || (!increasing && next >= stop)) {
result.push(next);
next = next + step;
}
return new ArrayWrapper(result);
}
exports.range = range;