set-array.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. type Key = string | number | symbol;
  2. /**
  3. * SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the
  4. * index of the `key` in the backing array.
  5. *
  6. * This is designed to allow synchronizing a second array with the contents of the backing array,
  7. * like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`,
  8. * and there are never duplicates.
  9. */
  10. export class SetArray<T extends Key = Key> {
  11. declare private _indexes: Record<T, number | undefined>;
  12. declare array: readonly T[];
  13. constructor() {
  14. this._indexes = { __proto__: null } as any;
  15. this.array = [];
  16. }
  17. }
  18. interface PublicSet<T extends Key> {
  19. array: T[];
  20. _indexes: SetArray<T>['_indexes'];
  21. }
  22. /**
  23. * Typescript doesn't allow friend access to private fields, so this just casts the set into a type
  24. * with public access modifiers.
  25. */
  26. function cast<T extends Key>(set: SetArray<T>): PublicSet<T> {
  27. return set as any;
  28. }
  29. /**
  30. * Gets the index associated with `key` in the backing array, if it is already present.
  31. */
  32. export function get<T extends Key>(setarr: SetArray<T>, key: T): number | undefined {
  33. return cast(setarr)._indexes[key];
  34. }
  35. /**
  36. * Puts `key` into the backing array, if it is not already present. Returns
  37. * the index of the `key` in the backing array.
  38. */
  39. export function put<T extends Key>(setarr: SetArray<T>, key: T): number {
  40. // The key may or may not be present. If it is present, it's a number.
  41. const index = get(setarr, key);
  42. if (index !== undefined) return index;
  43. const { array, _indexes: indexes } = cast(setarr);
  44. const length = array.push(key);
  45. return (indexes[key] = length - 1);
  46. }
  47. /**
  48. * Pops the last added item out of the SetArray.
  49. */
  50. export function pop<T extends Key>(setarr: SetArray<T>): void {
  51. const { array, _indexes: indexes } = cast(setarr);
  52. if (array.length === 0) return;
  53. const last = array.pop()!;
  54. indexes[last] = undefined;
  55. }
  56. /**
  57. * Removes the key, if it exists in the set.
  58. */
  59. export function remove<T extends Key>(setarr: SetArray<T>, key: T): void {
  60. const index = get(setarr, key);
  61. if (index === undefined) return;
  62. const { array, _indexes: indexes } = cast(setarr);
  63. for (let i = index + 1; i < array.length; i++) {
  64. const k = array[i];
  65. array[i - 1] = k;
  66. indexes[k]!--;
  67. }
  68. indexes[key] = undefined;
  69. array.pop();
  70. }