« get me outta code hell

content: generateDividedTrackList: context groups - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/common-util
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2026-04-02 23:27:55 -0300
committer(quasar) nebula <qznebula@protonmail.com>2026-04-02 23:27:55 -0300
commit818e89c7ee0a426ac5f66a4079c70e047627a7f2 (patch)
tree489d6651767e7b7214324e8dfbc47fed1d2deda6 /src/common-util
parentb250e91a74b244cc5decd79f5604cfb8a811421a (diff)
content: generateDividedTrackList: context groups
Diffstat (limited to 'src/common-util')
-rw-r--r--src/common-util/sugar.js32
-rw-r--r--src/common-util/wiki-data.js51
2 files changed, 80 insertions, 3 deletions
diff --git a/src/common-util/sugar.js b/src/common-util/sugar.js
index c988156c..26f33c20 100644
--- a/src/common-util/sugar.js
+++ b/src/common-util/sugar.js
@@ -229,6 +229,38 @@ export const mapInPlace = (array, fn) =>
 
 export const unique = (arr) => Array.from(new Set(arr));
 
+export function* permutations(array) {
+  switch (array.length) {
+    case 0: return;
+    case 1: yield array; return;
+    default: {
+      const behind = [];
+      const ahead = array.slice();
+      while (ahead.length) {
+        const here = ahead.shift();
+
+        yield*
+          permutations([...behind, ...ahead])
+            .map(slice => [here, ...slice]);
+
+        behind.push(here);
+      }
+    }
+  }
+}
+
+export function* runs(array) {
+  switch (array.length) {
+    case 0: return;
+    case 1: yield array; return;
+    default: {
+      yield* runs(array.slice(1)).map(run => [array[0], ...run]);
+      yield [array[0]];
+      yield* runs(array.slice(1));
+    }
+  }
+}
+
 export const compareArrays = (arr1, arr2, {checkOrder = true} = {}) =>
   arr1.length === arr2.length &&
   (checkOrder
diff --git a/src/common-util/wiki-data.js b/src/common-util/wiki-data.js
index 74222e9e..21e15725 100644
--- a/src/common-util/wiki-data.js
+++ b/src/common-util/wiki-data.js
@@ -506,8 +506,23 @@ export class TupleMap {
 }
 
 export class TupleMapForBabies {
-  #here = new WeakMap();
-  #next = new WeakMap();
+  #here;
+  #next;
+  #mode;
+
+  constructor(mode = 'weak') {
+    if (mode === 'weak') {
+      this.#mode = 'weak';
+      this.#here = new WeakMap();
+      this.#next = new WeakMap();
+    } else if (mode === 'strong') {
+      this.#mode = 'strong';
+      this.#here = new Map();
+      this.#next = new Map();
+    } else {
+      throw new Error(`Expected mode to be weak or strong`);
+    }
+  }
 
   set(...args) {
     const first = args.at(0);
@@ -519,7 +534,7 @@ export class TupleMapForBabies {
     } else if (this.#next.has(first)) {
       this.#next.get(first).set(...rest, last);
     } else {
-      const tupleMap = new TupleMapForBabies();
+      const tupleMap = new TupleMapForBabies(this.#mode);
       this.#next.set(first, tupleMap);
       tupleMap.set(...rest, last);
     }
@@ -550,6 +565,36 @@ export class TupleMapForBabies {
       return false;
     }
   }
+
+  *keys() {
+    if (this.#mode === 'weak') {
+      throw new Error(`Can't get keys of a weak tuple map`);
+    }
+
+    for (const key of this.#here.keys()) {
+      yield [key];
+
+      if (this.#next.has(key)) {
+        for (const next of this.#next.get(key).keys()) {
+          yield [key, ...next];
+        }
+      }
+    }
+  }
+
+  *values() {
+    if (this.#mode === 'weak') {
+      throw new Error(`Can't get values of a weak tuple map`);
+    }
+
+    for (const key of this.#here.keys()) {
+      yield this.#here.get(key);
+
+      if (this.#next.has(key)) {
+        yield* this.#next.get(key).values();
+      }
+    }
+  }
 }
 
 const combinedWikiDataTupleMap = new TupleMapForBabies();