2016-03-13 05:19:24 +08:00
|
|
|
export class Utils {
|
2019-07-28 04:56:12 +08:00
|
|
|
static GUID() {
|
|
|
|
const s4 = function () {
|
|
|
|
return Math.floor((1 + Math.random()) * 0x10000)
|
|
|
|
.toString(16)
|
|
|
|
.substring(1);
|
|
|
|
};
|
|
|
|
|
|
|
|
return s4() + s4() + '-' + s4() + s4();
|
|
|
|
}
|
|
|
|
|
2018-12-02 19:22:05 +08:00
|
|
|
static chunkArrays<T>(arr: T[], chunkSize: number): T[][] {
|
|
|
|
const R = [];
|
|
|
|
for (let i = 0; i < arr.length; i += chunkSize) {
|
|
|
|
R.push(arr.slice(i, i + chunkSize));
|
|
|
|
}
|
|
|
|
return R;
|
|
|
|
}
|
2016-03-13 05:19:24 +08:00
|
|
|
|
2018-12-08 05:06:13 +08:00
|
|
|
static wait(time: number) {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
setTimeout(resolve, time);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-07-07 18:19:08 +08:00
|
|
|
|
2021-04-10 16:51:33 +08:00
|
|
|
static removeNullOrEmptyObj<T extends { [key: string]: any }>(obj: T): T {
|
2018-12-06 00:29:33 +08:00
|
|
|
if (typeof obj !== 'object' || obj == null) {
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
const keys = Object.keys(obj);
|
|
|
|
for (let i = 0; i < keys.length; i++) {
|
2021-04-10 16:51:33 +08:00
|
|
|
const key: string = keys[i];
|
2018-12-06 00:29:33 +08:00
|
|
|
if (obj[key] !== null && typeof obj[key] === 'object') {
|
|
|
|
if (Utils.removeNullOrEmptyObj(obj[key])) {
|
|
|
|
if (Object.keys(obj[key]).length === 0) {
|
|
|
|
delete obj[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (obj[key] === null) {
|
|
|
|
delete obj[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
static clone<T>(object: T): T {
|
|
|
|
return JSON.parse(JSON.stringify(object));
|
|
|
|
}
|
|
|
|
|
2018-11-29 06:49:33 +08:00
|
|
|
static zeroPrefix(value: string | number, length: number) {
|
2018-11-18 03:15:48 +08:00
|
|
|
const ret = '00000' + value;
|
|
|
|
return ret.substr(ret.length - length);
|
|
|
|
}
|
|
|
|
|
2021-02-14 19:57:05 +08:00
|
|
|
/**
|
|
|
|
* Checks if the two input (let them be objects or arrays or just primitives) are equal
|
|
|
|
* @param object
|
|
|
|
* @param filter
|
|
|
|
*/
|
2017-06-11 04:32:56 +08:00
|
|
|
static equalsFilter(object: any, filter: any): boolean {
|
2018-05-13 00:19:51 +08:00
|
|
|
if (typeof filter !== 'object' || filter == null) {
|
|
|
|
return object === filter;
|
2017-07-15 18:47:11 +08:00
|
|
|
}
|
2018-05-29 02:03:12 +08:00
|
|
|
if (!object) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-05 02:37:47 +08:00
|
|
|
|
|
|
|
if (Array.isArray(object) && object.length !== filter.length) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-07-08 18:43:42 +08:00
|
|
|
const keys = Object.keys(filter);
|
2017-06-11 04:32:56 +08:00
|
|
|
for (let i = 0; i < keys.length; i++) {
|
2017-07-08 18:43:42 +08:00
|
|
|
const key = keys[i];
|
2018-05-13 00:19:51 +08:00
|
|
|
if (typeof filter[key] === 'object') {
|
|
|
|
if (Utils.equalsFilter(object[key], filter[key]) === false) {
|
2017-07-08 18:43:42 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (object[key] !== filter[key]) {
|
2017-06-11 04:32:56 +08:00
|
|
|
return false;
|
2017-07-08 18:43:42 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
2016-03-13 05:19:24 +08:00
|
|
|
}
|
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
return true;
|
|
|
|
}
|
2016-07-07 18:19:08 +08:00
|
|
|
|
2020-12-31 19:35:28 +08:00
|
|
|
static renderDataSize(size: number) {
|
|
|
|
const postFixes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
|
|
let index = 0;
|
|
|
|
while (size > 1000 && index < postFixes.length - 1) {
|
|
|
|
size /= 1000;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
return size.toFixed(2) + postFixes[index];
|
|
|
|
}
|
|
|
|
|
2016-03-26 23:25:48 +08:00
|
|
|
|
2018-11-02 23:24:37 +08:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param from
|
|
|
|
* @param to inclusive
|
|
|
|
* @returns {Array}
|
|
|
|
*/
|
|
|
|
static createRange(from: number, to: number): Array<number> {
|
|
|
|
const arr = new Array(to - from + 1);
|
|
|
|
let c = to - from + 1;
|
|
|
|
while (c--) {
|
|
|
|
arr[c] = to--;
|
|
|
|
}
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
2020-01-08 05:17:54 +08:00
|
|
|
public static canonizePath(path: string) {
|
|
|
|
return path
|
|
|
|
.replace(new RegExp('\\\\', 'g'), '/')
|
|
|
|
.replace(new RegExp('/+', 'g'), '/');
|
|
|
|
}
|
|
|
|
|
2017-07-20 02:47:09 +08:00
|
|
|
static concatUrls(...args: Array<string>) {
|
2018-05-13 00:19:51 +08:00
|
|
|
let url = '';
|
2017-06-11 04:32:56 +08:00
|
|
|
for (let i = 0; i < args.length; i++) {
|
2018-05-13 00:19:51 +08:00
|
|
|
if (args[i] === '' || typeof args[i] === 'undefined') {
|
|
|
|
continue;
|
|
|
|
}
|
2016-03-26 23:25:48 +08:00
|
|
|
|
2019-01-19 20:00:38 +08:00
|
|
|
const part = args[i].replace(new RegExp('\\\\', 'g'), '/');
|
2018-05-13 00:19:51 +08:00
|
|
|
if (part === '/' || part === './') {
|
|
|
|
continue;
|
|
|
|
}
|
2016-04-22 19:23:44 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
url += part + '/';
|
2016-05-13 00:24:26 +08:00
|
|
|
}
|
2019-01-19 20:00:38 +08:00
|
|
|
url = url.replace(new RegExp('/+', 'g'), '/');
|
2017-06-11 04:32:56 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
if (url.trim() === '') {
|
|
|
|
url = './';
|
2017-07-20 02:47:09 +08:00
|
|
|
}
|
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
return url.substring(0, url.length - 1);
|
|
|
|
}
|
|
|
|
|
2017-07-20 02:47:09 +08:00
|
|
|
public static updateKeys(targetObject: any, sourceObject: any) {
|
2017-06-11 04:32:56 +08:00
|
|
|
Object.keys(sourceObject).forEach((key) => {
|
2018-05-13 00:19:51 +08:00
|
|
|
if (typeof targetObject[key] === 'undefined') {
|
2017-06-11 04:32:56 +08:00
|
|
|
return;
|
|
|
|
}
|
2018-05-13 00:19:51 +08:00
|
|
|
if (typeof targetObject[key] === 'object') {
|
2017-06-11 04:32:56 +08:00
|
|
|
Utils.updateKeys(targetObject[key], sourceObject[key]);
|
|
|
|
} else {
|
|
|
|
targetObject[key] = sourceObject[key];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-07-20 02:47:09 +08:00
|
|
|
public static setKeys(targetObject: any, sourceObject: any) {
|
2017-06-11 04:32:56 +08:00
|
|
|
Object.keys(sourceObject).forEach((key) => {
|
2018-05-13 00:19:51 +08:00
|
|
|
if (typeof targetObject[key] === 'object') {
|
2017-06-11 04:32:56 +08:00
|
|
|
Utils.setKeys(targetObject[key], sourceObject[key]);
|
|
|
|
} else {
|
|
|
|
targetObject[key] = sourceObject[key];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-07-20 02:47:09 +08:00
|
|
|
public static setKeysForced(targetObject: any, sourceObject: any) {
|
2017-06-11 04:32:56 +08:00
|
|
|
Object.keys(sourceObject).forEach((key) => {
|
2018-05-13 00:19:51 +08:00
|
|
|
if (typeof sourceObject[key] === 'object') {
|
|
|
|
if (typeof targetObject[key] === 'undefined') {
|
2017-06-11 04:32:56 +08:00
|
|
|
targetObject[key] = {};
|
2016-05-02 03:30:43 +08:00
|
|
|
}
|
2017-06-11 04:32:56 +08:00
|
|
|
Utils.setKeysForced(targetObject[key], sourceObject[key]);
|
|
|
|
} else {
|
|
|
|
targetObject[key] = sourceObject[key];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-05 02:37:47 +08:00
|
|
|
public static enumToArray(EnumType: any): { key: number; value: string }[] {
|
2018-05-13 00:19:51 +08:00
|
|
|
const arr: Array<{ key: number; value: string; }> = [];
|
|
|
|
for (const enumMember in EnumType) {
|
2017-06-11 04:32:56 +08:00
|
|
|
if (!EnumType.hasOwnProperty(enumMember)) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-05-13 00:19:51 +08:00
|
|
|
const key = parseInt(enumMember, 10);
|
2017-06-11 04:32:56 +08:00
|
|
|
if (key >= 0) {
|
|
|
|
arr.push({key: key, value: EnumType[enumMember]});
|
|
|
|
}
|
2016-05-02 03:30:43 +08:00
|
|
|
}
|
2017-06-11 04:32:56 +08:00
|
|
|
return arr;
|
|
|
|
}
|
2016-04-22 19:23:44 +08:00
|
|
|
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2019-12-15 00:27:01 +08:00
|
|
|
public static findClosest(number: number, arr: number[]): number {
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
let curr = arr[0];
|
|
|
|
let diff = Math.abs(number - curr);
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
arr.forEach((value) => {
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
const newDiff = Math.abs(number - value);
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
if (newDiff < diff) {
|
|
|
|
diff = newDiff;
|
|
|
|
curr = value;
|
|
|
|
}
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
});
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
return curr;
|
|
|
|
}
|
2016-05-12 17:00:46 +08:00
|
|
|
|
2019-12-15 00:27:01 +08:00
|
|
|
|
|
|
|
public static findClosestinSorted(number: number, arr: number[]): number {
|
|
|
|
|
|
|
|
let curr = arr[0];
|
|
|
|
let diff = Math.abs(number - curr);
|
|
|
|
for (let i = 0; i < arr.length; ++i) {
|
|
|
|
|
|
|
|
const newDiff = Math.abs(number - arr[i]);
|
|
|
|
if (newDiff > diff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
diff = newDiff;
|
|
|
|
curr = arr[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return curr;
|
|
|
|
}
|
|
|
|
|
2019-02-03 11:22:51 +08:00
|
|
|
public static isUInt32(value: number, max: number = 4294967295) {
|
2021-04-02 04:17:40 +08:00
|
|
|
value = parseInt('' + value, 10);
|
2019-02-03 11:22:51 +08:00
|
|
|
return !isNaN(value) && value >= 0 && value <= max;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static isInt32(value: number) {
|
2021-04-02 04:17:40 +08:00
|
|
|
value = parseFloat('' + value);
|
2019-02-03 11:22:51 +08:00
|
|
|
return !isNaN(value) && value >= -2147483648 && value <= 2147483647;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static isFloat32(value: number) {
|
|
|
|
const E = Math.pow(10, 38);
|
|
|
|
const nE = Math.pow(10, -38);
|
|
|
|
return !isNaN(value) && ((value >= -3.402823466 * E && value <= -1.175494351 * nE) ||
|
|
|
|
(value <= 3.402823466 * E && value >= 1.175494351 * nE));
|
|
|
|
}
|
|
|
|
|
2021-01-17 06:37:14 +08:00
|
|
|
public static getAnyX(num: number, arr: any[], start = 0): any[][] {
|
|
|
|
if (num <= 0 || num > arr.length || start >= arr.length) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
if (num <= 1) {
|
|
|
|
return arr.slice(start).map(e => [e]);
|
|
|
|
}
|
|
|
|
if (num === arr.length - start) {
|
|
|
|
return [arr.slice(start)];
|
|
|
|
}
|
|
|
|
const ret: any[][] = [];
|
|
|
|
for (let i = start; i < arr.length; ++i) {
|
|
|
|
Utils.getAnyX(num - 1, arr, i + 1).forEach(a => {
|
|
|
|
a.push(arr[i]);
|
|
|
|
ret.push(a);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-03-13 05:19:24 +08:00
|
|
|
}
|
2021-04-06 17:32:31 +08:00
|
|
|
|
|
|
|
export namespace Utils {
|
|
|
|
export class LRU<V> {
|
|
|
|
data: { [key: string]: { value: V, usage: number } } = {};
|
|
|
|
|
|
|
|
|
|
|
|
constructor(public readonly size: number) {
|
|
|
|
}
|
|
|
|
|
|
|
|
set(key: string, value: V): void {
|
|
|
|
this.data[key] = {usage: Date.now(), value: value};
|
|
|
|
if (Object.keys(this.data).length > this.size) {
|
|
|
|
let oldestK = key;
|
|
|
|
let oldest = this.data[oldestK].usage;
|
|
|
|
for (const k in this.data) {
|
|
|
|
if (this.data[k].usage < oldest) {
|
|
|
|
oldestK = k;
|
|
|
|
oldest = this.data[oldestK].usage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete this.data[oldestK];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get(key: string): V {
|
|
|
|
if (!this.data[key]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return this.data[key].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|