@kwiz/common
Version:
KWIZ common utilities and helpers for M365 platform
408 lines • 16.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.numbersArray = exports.FindNextAvailableName = exports.ArrayFill = exports.PushNoDuplicate = exports.RemoveItemFromArr = exports.defaultPrimitiveGetValue = exports.GetPagedCollectionInfo = exports.FlattenGroupItems = exports.IsMultiLevelGroup = exports.GetUniqueArrayInfo = exports.makeUniqueArray = exports.flattenArray = exports.convertDictionary = exports.toArray = exports.toHash = exports.chunkArray = exports.sizeOf = exports.forEachAsync = exports.forEach = exports.sortNumberArray = exports.sortNumberArrayAsc = exports.filterEmptyEntries = exports.sortArray = exports.lastOrNullAsync = exports.lastIndexOfAsync = exports.firstOrNullAsync = exports.firstIndexOfAsync = exports.lastOrNull = exports.lastIndexOf = exports.firstOrNull = exports.firstIndexOf = exports.moveToEnd = exports.moveToStart = void 0;
const typecheckers_1 = require("./typecheckers");
/** Finds an object in array based on a filter and moves it to the start of the array */
function moveToStart(arr, filter) {
let index = firstIndexOf(arr, filter);
if (index > 0) {
let obj = arr[index];
arr.splice(index, 1);
arr.unshift(obj);
}
}
exports.moveToStart = moveToStart;
/** Finds an object in array based on a filter and moves it to the end of the array */
function moveToEnd(arr, filter) {
let index = firstIndexOf(arr, filter);
if (index !== -1 && index !== arr.length - 1) {
let obj = arr[index];
arr.splice(index, 1);
arr.push(obj);
}
}
exports.moveToEnd = moveToEnd;
/** Get the first index of an object of an array, or -1 if the array is empty / null */
// export function firstIndexOf<T extends Element>(arr: HTMLCollectionOf<T>, filter?: (item: T, index?: number) => boolean, startFrom?: number): number;
// export function firstIndexOf<T>(arr: T[], filter?: (item: T, index?: number) => boolean, startFrom?: number): number;
function firstIndexOf(arr, filter, startFrom) {
if (!(0, typecheckers_1.isNullOrUndefined)(arr) && arr.length > 0) {
if ((0, typecheckers_1.isFunction)(filter)) {
//use for loop so we can stop when it is found
for (let i = startFrom > 0 ? startFrom : 0; i < arr.length; i++)
if (filter(arr[i], i) === true)
return i;
}
else
return 0;
}
return -1;
}
exports.firstIndexOf = firstIndexOf;
/** Get the first object of an array, or null if the array is empty / null
* If you pass a filter, it will find the first element that matches the filter and return it, stopping the loop when it is found
* */
function firstOrNull(arr, filter) {
let index = firstIndexOf(arr, filter);
return index < 0 ? null : arr[index];
}
exports.firstOrNull = firstOrNull;
/** Get the last index of an object of an array, or -1 if the array is empty / null */
function lastIndexOf(arr, filter) {
if (!(0, typecheckers_1.isNullOrUndefined)(arr) && arr.length > 0) {
if ((0, typecheckers_1.isFunction)(filter)) {
//use for loop so we can stop when it is found
for (let i = arr.length - 1; i >= 0; i--)
if (filter(arr[i]) === true)
return i;
}
else
return arr.length - 1;
}
return -1;
}
exports.lastIndexOf = lastIndexOf;
/** get the last element or null */
function lastOrNull(arr, filter) {
let index = lastIndexOf(arr, filter);
return index < 0 ? null : arr[index];
}
exports.lastOrNull = lastOrNull;
/** Get the first index of an object of an array, or -1 if the array is empty / null */
async function firstIndexOfAsync(arr, filter, startFrom) {
if (!(0, typecheckers_1.isNullOrUndefined)(arr) && arr.length > 0) {
if ((0, typecheckers_1.isFunction)(filter)) {
//use for loop so we can stop when it is found
for (let i = startFrom > 0 ? startFrom : 0; i < arr.length; i++)
if ((await filter(arr[i], i)) === true)
return i;
}
else
return 0;
}
return -1;
}
exports.firstIndexOfAsync = firstIndexOfAsync;
/** Get the first object of an array, or null if the array is empty / null
* If you pass a filter, it will find the first element that matches the filter and return it, stopping the loop when it is found
* */
async function firstOrNullAsync(arr, filter) {
let index = await firstIndexOfAsync(arr, filter);
return index < 0 ? null : arr[index];
}
exports.firstOrNullAsync = firstOrNullAsync;
/** Get the last index of an object of an array, or -1 if the array is empty / null */
async function lastIndexOfAsync(arr, filter) {
if (!(0, typecheckers_1.isNullOrUndefined)(arr) && arr.length > 0) {
if ((0, typecheckers_1.isFunction)(filter)) {
//use for loop so we can stop when it is found
for (let i = arr.length - 1; i >= 0; i--)
if ((await filter(arr[i])) === true)
return i;
}
else
return arr.length - 1;
}
return -1;
}
exports.lastIndexOfAsync = lastIndexOfAsync;
/** get the last element or null */
async function lastOrNullAsync(arr, filter) {
let index = await lastIndexOfAsync(arr, filter);
return index < 0 ? null : arr[index];
}
exports.lastOrNullAsync = lastOrNullAsync;
/** Sorts an array of complex objects, use defaultPrimitiveGetValue for default functionality */
function sortArray(arr, getValue) {
if (!(0, typecheckers_1.isNullOrEmptyArray)(arr)) {
if ((0, typecheckers_1.isTypeofFullNameFunction)("Intl.Collator")) {
//todo: should probably use the SharePoint locale isntead of 'undefined'
let collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
arr.sort((a, b) => {
let va = getValue(a);
let vb = getValue(b);
return collator.compare(va, vb);
});
}
else {
arr.sort((a, b) => {
let va = getValue(a);
if ((0, typecheckers_1.isString)(va))
va = va.toLowerCase();
let vb = getValue(b);
if ((0, typecheckers_1.isString)(vb))
vb = vb.toLowerCase();
return va === vb ? 0 : va > vb ? 1 : -1;
});
}
}
return arr;
}
exports.sortArray = sortArray;
/** removes null, undefined or "" elements from the array */
function filterEmptyEntries(arr) {
return arr.filter(val => !(0, typecheckers_1.isNullOrEmptyString)(val));
}
exports.filterEmptyEntries = filterEmptyEntries;
function sortNumberArrayAsc(a, b) {
return a - b;
}
exports.sortNumberArrayAsc = sortNumberArrayAsc;
function sortNumberArray(a, b) {
return b - a;
}
exports.sortNumberArray = sortNumberArray;
/** call a foreach on an object or an array, with an option to break when returning false */
function forEach(obj, func, args) {
if (obj && func && (0, typecheckers_1.isFunction)(func)) {
if (Array.isArray(obj) || obj.constructor && (0, typecheckers_1.getFromFullName)("constructor.name", obj) === "Array") {
for (let i = 0; i < obj.length; i++) {
let property = i;
let value = obj[property];
let result = func(property.toString(10), value, args);
if (result === false) {
break;
}
}
}
else {
let keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
let property = keys[i];
let value = obj[property];
let result = func(property, value, args);
if (result === false) {
break;
}
}
}
}
}
exports.forEach = forEach;
async function forEachAsync(arr, handler, options) {
if (!(0, typecheckers_1.isNullOrUndefined)(arr) && Object.keys(arr).length > 0) {
let keys = Object.keys(arr);
if (options && options.parallel) {
let promises = [];
keys.forEach((key, i) => {
promises.push(handler(arr[key], i));
});
return Promise.all(promises);
}
else {
let results = [];
for (let i = 0; i < keys.length; i++) {
results.push(await handler(arr[keys[i]], i));
}
return results;
}
}
else
return [];
}
exports.forEachAsync = forEachAsync;
function sizeOf(obj) {
if (Array.isArray(obj))
return obj.length;
return Object.keys(obj).length;
}
exports.sizeOf = sizeOf;
function chunkArray(array, chunkSize) {
let chunkedArray = [];
for (var i = 0; i < array.length; i += chunkSize) {
chunkedArray.push(array.slice(i, i + chunkSize));
}
return chunkedArray;
}
exports.chunkArray = chunkArray;
/** Takes an array and transforms it into a hash. this will assign 1 item per key, assumig getKey will be unique per item. */
function toHash(arr, getKey, filter, transformValue) {
let hash = {};
if (!(0, typecheckers_1.isFunction)(transformValue))
transformValue = v => v;
if ((0, typecheckers_1.isNotEmptyArray)(arr))
arr.forEach(i => {
if (!(0, typecheckers_1.isFunction)(filter) || filter(i))
hash[getKey(i)] = transformValue(i);
});
return hash;
}
exports.toHash = toHash;
/** Returns an array from the values of the dictionary. */
function toArray(hash, filter, transform) {
let arr = [];
if (!(0, typecheckers_1.isFunction)(transform))
transform = (key, element) => element;
if (!(0, typecheckers_1.isNullOrUndefined)(hash))
Object.keys(hash).forEach(key => {
if (!(0, typecheckers_1.isFunction)(filter) || filter(hash[key]))
arr.push(transform(key, hash[key]));
});
return arr;
}
exports.toArray = toArray;
/** returns a new dictionary, converting each entry in source using the transform function */
function convertDictionary(source, transform) {
let result = {};
forEach(source, (key, value) => { result[key] = transform(value); });
return result;
}
exports.convertDictionary = convertDictionary;
function flattenArray(array) {
return array.reduce((acc, val) => acc.concat(val), []);
}
exports.flattenArray = flattenArray;
/** careful, does not work for date/complex objects. Use GetUniqueArrayInfo if you suspect you might have Date/complex objects. */
function makeUniqueArray(arr) {
return arr.filter((v, i, a) => a.indexOf(v) === i);
}
exports.makeUniqueArray = makeUniqueArray;
/** return an array of unique values, and the first index they were found, use defaultPrimitiveGetValue for default functionality */
function GetUniqueArrayInfo(arr, getValue) {
var uniqueValues = [];
var uniqueArray = [];
var foundValues = [];
var hasDuplicates = false;
var duplicateIndexes = [];
if ((0, typecheckers_1.isNotEmptyArray)(arr)) {
arr.forEach((item, index) => {
let value = getValue(item);
if (foundValues.includes(value)) {
hasDuplicates = true;
duplicateIndexes.push(index);
}
else {
foundValues.push(value);
uniqueValues.push({ item: item, value: value, firstIndex: index });
uniqueArray.push(item);
}
});
}
return {
/** true if duplicate values found */
hasDuplicates: hasDuplicates,
/** all duplicate item indexes */
duplicateIndexes: duplicateIndexes,
/** unique values and their info */
uniqueValues: uniqueValues,
/** the unique version of this array */
uniqueArray: uniqueArray
};
}
exports.GetUniqueArrayInfo = GetUniqueArrayInfo;
/** returns true if the element is a group of items */
function IsMultiLevelGroup(groupOrItem) {
let asGroup = groupOrItem;
return !(0, typecheckers_1.isNullOrUndefined)(asGroup.subGroups) && Array.isArray(asGroup.groupItems) && (0, typecheckers_1.isNumber)(asGroup.index) && (0, typecheckers_1.isNumber)(asGroup.depth);
}
exports.IsMultiLevelGroup = IsMultiLevelGroup;
/** returns a flat array of groups>items ordered by groups */
function FlattenGroupItems(groups) {
let flatItems = [];
Object.keys(groups).forEach(groupName => {
let group = groups[groupName];
if (!(0, typecheckers_1.isNullOrEmptyString)(groupName))
flatItems.push(group);
let subGroups = Object.keys(group.subGroups);
if ((0, typecheckers_1.isNotEmptyArray)(subGroups)) {
flatItems.push(...FlattenGroupItems(group.subGroups));
}
else
flatItems.push(...group.groupItems);
});
return flatItems;
}
exports.FlattenGroupItems = FlattenGroupItems;
/** split a collection by page size and return the info */
function GetPagedCollectionInfo(collection, pageSize, currentPage) {
let pagedItems = [];
if (pageSize < 1) {
pagedItems = [collection.slice()];
}
else {
let copy = collection.slice();
while ((0, typecheckers_1.isNotEmptyArray)(copy)) {
pagedItems.push(copy.splice(0, pageSize));
}
}
currentPage = (0, typecheckers_1.isNumber)(currentPage) && currentPage >= 0 && currentPage < pagedItems.length ? currentPage : 0;
return {
/** nubmer of pages */
pages: pagedItems.length,
/** page items, per page (Array of pages, each has an array of the page items) */
pagedItems: pagedItems,
/** the current page */
currentPage: currentPage,
/** the current page items */
currentPageItems: pagedItems[currentPage] || [],
/** has more than 1 page */
hasPages: pagedItems.length > 1,
allowPrev: currentPage > 0,
allowNext: currentPage < pagedItems.length - 1
};
}
exports.GetPagedCollectionInfo = GetPagedCollectionInfo;
/** use with sortArray or get unique array to handle premitive types or dates, with a JSON.stringify to all other values */
function defaultPrimitiveGetValue(item) {
return (0, typecheckers_1.isNullOrUndefined)(item)
? ""
: (0, typecheckers_1.isDate)(item) ? item.getTime()
: (0, typecheckers_1.isBoolean)(item)
? item === true ? 1 : 0
: (0, typecheckers_1.isNumber)(item) || (0, typecheckers_1.isString)(item)
? item
: JSON.stringify(item);
}
exports.defaultPrimitiveGetValue = defaultPrimitiveGetValue;
function RemoveItemFromArr(arr, item) {
let idx = arr.indexOf(item);
if (idx >= 0)
arr.splice(idx, 1);
}
exports.RemoveItemFromArr = RemoveItemFromArr;
function PushNoDuplicate(arr, item) {
if (!arr.includes(item))
arr.push(item);
}
exports.PushNoDuplicate = PushNoDuplicate;
/** fills an array with a value. Array.fill isn't available on SPFx. */
function ArrayFill(arr, value, onlyEmpty) {
for (let i = 0; i < arr.length; i++) {
if (onlyEmpty !== true || (0, typecheckers_1.isNullOrUndefined)(arr[i]))
arr[i] = value;
}
return arr;
}
exports.ArrayFill = ArrayFill;
/** give a name and a collection, and it will return a unique name availalbe, suffixing a _# to the name
* example: file
* return file, file_2, file_9 etc... whichever is availalbe first.
*/
function FindNextAvailableName(name, usedNames, options) {
let nameForTest = name;
if (options && options.caseSensitive !== true) {
usedNames = usedNames.map(n => n.toLowerCase());
nameForTest = name.toLowerCase();
}
let nameSuffix = options && options.suffix || "";
let suffixIdx = 0;
let suffixStr = "";
while (usedNames.indexOf(`${nameForTest}${suffixStr}${nameSuffix}`) >= 0) {
suffixIdx++;
suffixStr = "_" + suffixIdx;
}
return `${name}${suffixStr}${nameSuffix}`;
}
exports.FindNextAvailableName = FindNextAvailableName;
//** returns an array of numbers from 0,1,2... */
function numbersArray(length, startFrom = 0) {
//dvp build will fail without any type
if ((0, typecheckers_1.isNullOrUndefined)(length) || length < 0)
length = 0;
let arr = Array.from(Array(length).keys());
return startFrom > 0
? arr.map(i => i + startFrom)
: arr;
}
exports.numbersArray = numbersArray;
//# sourceMappingURL=collections.base.js.map