UNPKG

immutable-class

Version:

A template for creating immutable classes

137 lines (116 loc) 3.51 kB
/* * Copyright 2015-2019 Imply Data, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import hasOwnProp from 'has-own-prop'; import { SimpleArray } from '../simple-array/simple-array'; export type KeyGetter = (x: any) => string; export class KeyedArray<T> { public getKey: KeyGetter; constructor(keyGetter: KeyGetter) { this.getKey = keyGetter; } static withKey(key: string): any { return new KeyedArray((x: any) => x[key]); } public get(array: T[], key: string): T | undefined { const { getKey } = this; return SimpleArray.find(array, x => getKey(x) === key); } public toRecord(array: T[]): Record<string, T> { const { getKey } = this; const myRecord: Record<string, T> = {}; for (const a of array) { const key = getKey(a); if (myRecord[key]) continue; myRecord[key] = a; } return myRecord; } public checkValid(array: T[], what?: string, where?: string): void { const { getKey } = this; const seen: Record<string, number> = {}; for (const a of array) { const key = getKey(a); if (seen[key]) { throw new Error( ['duplicate', what, `'${key}'`, where ? 'in' : undefined, where] .filter(Boolean) .join(' '), ); } seen[key] = 1; } } public isValid(array: T[]): boolean { const { getKey } = this; const seen: Record<string, number> = {}; for (const a of array) { const key = getKey(a); if (seen[key]) return false; seen[key] = 1; } return true; } public overrideByKey(things: T[], thingOverride: T): T[] { const { getKey } = this; const overrideKey = getKey(thingOverride); let added = false; things = things.map(t => { if (getKey(t) === overrideKey) { added = true; return thingOverride; } else { return t; } }); if (!added) things.push(thingOverride); return things; } public overridesByKey(things: T[], thingOverrides: T[]): T[] { const { getKey } = this; const keyToIndex: Record<string, number> = {}; const thingsLength = things.length; for (let i = 0; i < thingsLength; i++) { keyToIndex[getKey(things[i])] = i; } const newThings = things.slice(); for (const thingOverride of thingOverrides) { const key = getKey(thingOverride); if (hasOwnProp(keyToIndex, key)) { newThings[keyToIndex[key]] = thingOverride; } else { newThings.push(thingOverride); } } return newThings; } public dedupe(array: T[]): T[] { const { getKey } = this; const seen: Record<string, boolean> = {}; return array.filter(a => { const key = getKey(a); if (seen[key]) { return false; } else { seen[key] = true; return true; } }); } public deleteByKey(array: T[], key: string): T[] { const { getKey } = this; return array.filter(a => getKey(a) !== key); } }