mirror of https://github.com/rancher/dashboard.git
257 lines
6.1 KiB
TypeScript
257 lines
6.1 KiB
TypeScript
import xor from 'lodash/xor';
|
|
import { get, isEqual } from '@shell/utils/object';
|
|
|
|
export function removeObject<T>(ary: T[], obj: T): T[] {
|
|
const idx = ary.indexOf(obj);
|
|
|
|
if ( idx >= 0 ) {
|
|
ary.splice(idx, 1);
|
|
}
|
|
|
|
return ary;
|
|
}
|
|
|
|
export function removeObjects<T>(ary: T[], objs: T[]): T[] {
|
|
let i;
|
|
let indexes = [];
|
|
|
|
for ( i = 0 ; i < objs.length ; i++ ) {
|
|
let idx = ary.indexOf(objs[i]);
|
|
|
|
// Find multiple copies of the same value
|
|
while ( idx !== -1 ) {
|
|
indexes.push(idx);
|
|
idx = ary.indexOf(objs[i], idx + 1);
|
|
}
|
|
}
|
|
|
|
if ( !indexes.length ) {
|
|
// That was easy...
|
|
return ary;
|
|
}
|
|
|
|
indexes = indexes.sort((a, b) => a - b);
|
|
|
|
const ranges = [];
|
|
let first: number;
|
|
let last: number;
|
|
|
|
// Group all the indexes into contiguous ranges
|
|
while ( indexes.length ) {
|
|
first = indexes.shift() as number;
|
|
last = first;
|
|
|
|
while ( indexes.length && indexes[0] === last + 1 ) {
|
|
last = indexes.shift() as number;
|
|
}
|
|
|
|
ranges.push({ start: first, end: last });
|
|
}
|
|
|
|
// Remove the items by range
|
|
for ( i = ranges.length - 1 ; i >= 0 ; i--) {
|
|
const { start, end } = ranges[i];
|
|
|
|
ary.splice(start, end - start + 1);
|
|
}
|
|
|
|
return ary;
|
|
}
|
|
|
|
export function addObject<T>(ary: T[], obj: T): void {
|
|
const idx = ary.indexOf(obj);
|
|
|
|
if ( idx === -1 ) {
|
|
ary.push(obj);
|
|
}
|
|
}
|
|
|
|
export function addObjects<T>(ary: T[], objs: T[]): void {
|
|
const unique: T[] = [];
|
|
|
|
for ( const obj of objs ) {
|
|
if ( !ary.includes(obj) && !unique.includes(obj) ) {
|
|
unique.push(obj);
|
|
}
|
|
}
|
|
|
|
ary.push(...unique);
|
|
}
|
|
|
|
export function insertAt<T>(ary: T[], idx: number, ...objs: T[]): void {
|
|
ary.splice(idx, 0, ...objs);
|
|
}
|
|
|
|
export function isArray<T>(thing: T[] | unknown): boolean {
|
|
return Array.isArray(thing);
|
|
}
|
|
|
|
export function removeAt<T>(ary: T[], idx: number, length = 1): T[] {
|
|
if ( idx < 0 ) {
|
|
throw new Error('Index too low');
|
|
}
|
|
|
|
if ( idx + length > ary.length ) {
|
|
throw new Error('Index + length too high');
|
|
}
|
|
|
|
ary.splice(idx, length);
|
|
|
|
return ary;
|
|
}
|
|
|
|
export function clear<T>(ary: T[]): void {
|
|
ary.splice(0, ary.length);
|
|
}
|
|
|
|
export function replaceWith<T>(ary: T[], ...values: T[]): void {
|
|
ary.splice(0, ary.length, ...values);
|
|
}
|
|
|
|
function findOrFilterBy<T, K, V>(
|
|
method: 'filter', ary: T[] | null, keyOrObj: string | K, val?: V
|
|
): T[];
|
|
function findOrFilterBy<T, K, V>(
|
|
method: 'find', ary: T[] | null, keyOrObj: string | K, val?: V
|
|
): T;
|
|
function findOrFilterBy<T, K, V>(
|
|
method: keyof T[], ary: T[] | null, keyOrObj: string | K, val?: V
|
|
): T[] {
|
|
ary = ary || [];
|
|
|
|
if ( typeof keyOrObj === 'object' ) {
|
|
return (ary[method] as Function)((item: T) => {
|
|
for ( const path in keyOrObj ) {
|
|
const want = keyOrObj[path];
|
|
const have = get(item, path);
|
|
|
|
if ( typeof want === 'undefined' ) {
|
|
if ( !have ) {
|
|
return false;
|
|
}
|
|
} else if ( have !== want ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
} else if ( val === undefined ) {
|
|
return (ary[method] as Function)((item: T) => !!get(item, keyOrObj));
|
|
} else {
|
|
return (ary[method] as Function)((item: T) => get(item, keyOrObj) === val);
|
|
}
|
|
}
|
|
|
|
export function filterBy<T, K, V>(
|
|
ary: T[] | null, keyOrObj: string | K, val?: V
|
|
): T[] {
|
|
return findOrFilterBy('filter', ary, keyOrObj, val);
|
|
}
|
|
|
|
export function findBy<T, K, V>(
|
|
ary: T[] | null, keyOrObj: string | K, val?: V
|
|
): T {
|
|
return findOrFilterBy('find', ary, keyOrObj, val);
|
|
}
|
|
|
|
export function findStringIndex(items: string[], item: string, trim = true): number {
|
|
return items.indexOf(trim ? item?.trim() : item);
|
|
}
|
|
|
|
export function hasDuplicatedStrings(items: string[], caseSensitive = true): boolean {
|
|
const normalizedItems = items.map((i) => (caseSensitive ? i : i.toLowerCase()).trim());
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
const index = findStringIndex(
|
|
normalizedItems,
|
|
(caseSensitive ? items[i] : items[i].toLowerCase()),
|
|
);
|
|
|
|
if (i !== index) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export function sameContents<T>(aryA: T[], aryB: T[]): boolean {
|
|
return xor(aryA, aryB).length === 0;
|
|
}
|
|
|
|
export function sameArrayObjects<T>(aryA: T[], aryB: T[], positionAgnostic = false): boolean {
|
|
if (!aryA && !aryB) {
|
|
// catch calls from js (where props aren't type checked)
|
|
return false;
|
|
}
|
|
if (aryA?.length !== aryB?.length) {
|
|
// catch one null and not t'other, and different lengths
|
|
return false;
|
|
}
|
|
|
|
if (positionAgnostic) {
|
|
const consumedB: { [pos: number]: boolean } = {};
|
|
|
|
aryB.forEach((_, index) => {
|
|
consumedB[index] = false;
|
|
});
|
|
|
|
for (let i = 0; i < aryA.length; i++) {
|
|
const a = aryA[i];
|
|
|
|
const validA = aryB.findIndex((arB, index) => isEqual(arB, a) && !consumedB[index] );
|
|
|
|
if (validA >= 0) {
|
|
consumedB[validA] = true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
for (let i = 0; i < aryA.length; i++) {
|
|
if (!isEqual(aryA[i], aryB[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export function uniq<T>(ary: T[]): T[] {
|
|
const out: T[] = [];
|
|
|
|
addObjects(out, ary);
|
|
|
|
return out;
|
|
}
|
|
|
|
export function concatStrings(a: string[], b: string[]): string[] {
|
|
return [...a.map((aa) => b.map((bb) => aa.concat(bb)))].reduce((acc, arr) => [...arr, ...acc], []);
|
|
}
|
|
|
|
interface KubeResource { metadata: { labels: { [name: string]: string} } } // Migrate to central kube types resource when those are brought in
|
|
export function getUniqueLabelKeys<T extends KubeResource>(aryResources: T[]): string[] {
|
|
const uniqueObj = aryResources.reduce((res, r) => {
|
|
Object.keys(r.metadata.labels).forEach((l) => (res[l] = true));
|
|
|
|
return res;
|
|
}, {} as {[label: string]: boolean});
|
|
|
|
return Object.keys(uniqueObj).sort();
|
|
}
|
|
|
|
/**
|
|
* Join list as string into a new string without duplicates
|
|
* @param {string} a 'a b c'
|
|
* @param {string} b 'b c d'
|
|
* @param {string} [separator=' ']
|
|
* @return {string} 'a b c d'
|
|
*/
|
|
export const joinStringList = (a: string, b: string, separator = ' '): string => {
|
|
const all = a.split(separator).concat(b.split(separator));
|
|
|
|
return [...new Set(all)].join(separator);
|
|
};
|