@genome-spy/core 0.61.1 → 0.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,64 +1,34 @@
1
1
  import FlatQueue from "flatqueue";
2
2
 
3
3
  /**
4
- * Finds the top k
5
- *
6
- * Based on ideas at https://lemire.me/blog/2017/06/21/top-speed-for-top-k-queries/
4
+ * Finds the top k elements in a slice of the data array, using a priority accessor.
7
5
  *
8
6
  * @param {T[]} data
9
7
  * @param {number} k
10
8
  * @param {(datum: T) => number} priorityAccessor
11
- * @template T
12
- */
13
- export function topK(data, k, priorityAccessor) {
14
- /** @type {FlatQueue<number>} */
15
- const queue = new FlatQueue();
16
-
17
- let i;
18
- for (i = 0; i < k && i < data.length; i++) {
19
- queue.push(i, priorityAccessor(data[i]));
20
- }
21
-
22
- for (; i < data.length; i++) {
23
- const p = priorityAccessor(data[i]);
24
- if (p >= queue.peekValue()) {
25
- queue.push(i, p);
26
- queue.pop();
27
- }
28
- }
29
-
30
- const result = [];
31
-
32
- let index;
33
- while ((index = queue.pop()) !== undefined) {
34
- result.push(data[index]);
35
- }
36
-
37
- return result.reverse();
38
- }
39
-
40
- /**
41
- * Takes an array of priorities and returns the top k indices from the
42
- * specified slice
43
- *
44
- * @param {number[]} priorities An array of priorities
45
- * @param {number} k
46
9
  * @param {number} [start] Default: 0
47
- * @param {number} [end] Exclusive. Default: priorities.length
10
+ * @param {number} [end] Exclusive. Default: data.length
11
+ * @template T
12
+ * @returns {T[]}
48
13
  */
49
- export function topKSlice(priorities, k, start = 0, end = priorities.length) {
14
+ export function topK(
15
+ data,
16
+ k,
17
+ priorityAccessor = (x) => +x,
18
+ start = 0,
19
+ end = data.length
20
+ ) {
50
21
  /** @type {FlatQueue<number>} */
51
22
  const queue = new FlatQueue();
52
-
53
23
  const sliceLength = end - start;
54
24
 
55
25
  let i;
56
26
  for (i = 0; i < k && i < sliceLength; i++) {
57
- queue.push(i, priorities[start + i]);
27
+ queue.push(i, priorityAccessor(data[start + i]));
58
28
  }
59
29
 
60
30
  for (; i < sliceLength; i++) {
61
- const p = priorities[start + i];
31
+ const p = priorityAccessor(data[start + i]);
62
32
  if (p >= queue.peekValue()) {
63
33
  queue.push(i, p);
64
34
  queue.pop();
@@ -66,10 +36,9 @@ export function topKSlice(priorities, k, start = 0, end = priorities.length) {
66
36
  }
67
37
 
68
38
  const result = [];
69
-
70
39
  let index;
71
40
  while ((index = queue.pop()) !== undefined) {
72
- result.push(start + index);
41
+ result.push(data[start + index]);
73
42
  }
74
43
 
75
44
  return result.reverse();
@@ -1,6 +1,5 @@
1
1
  import { expect, test } from "vitest";
2
- import { range } from "d3-array";
3
- import { topK, topKSlice } from "./topK.js";
2
+ import { topK } from "./topK.js";
4
3
 
5
4
  test("topK returns top k numbers in priority order", () => {
6
5
  /** @param {number} x */
@@ -16,49 +15,40 @@ test("topK returns top k numbers in priority order", () => {
16
15
  expect(topK([1, 1, 1], 3, priorityAccessor)).toEqual([1, 1, 1]);
17
16
  });
18
17
 
19
- test("topK returns top k objects in priority order", () => {
20
- /** @param {{priority: number}} d */
21
- const priorityAccessor = (d) => d.priority;
18
+ test("topK returns top k objects in priority order within a start-end range", () => {
19
+ const arr = [0, 9, 1, 8, 2, 7, 3, 6, 4, 5].map((x) => ({ priority: x }));
20
+ const priorityAccessor = (/** @type {{priority: number}} */ d) =>
21
+ d.priority;
22
22
 
23
- expect(
24
- topK(
25
- [0, 9, 1, 8, 2, 7, 3, 6, 4, 5].map((x) => ({ priority: x })),
26
- 3,
27
- priorityAccessor
28
- )
29
- ).toEqual([9, 8, 7].map((x) => ({ priority: x })));
30
- });
31
-
32
- test("topK returns top k objects in priority order with large datasets", () => {
33
- /** @param {number} x */
34
- const priorityAccessor = (x) => x;
23
+ // Range: indices 2 to 8 (1,8,2,7,3,6)
24
+ expect(topK(arr, 2, priorityAccessor, 2, 8)).toEqual([
25
+ { priority: 8 },
26
+ { priority: 7 },
27
+ ]);
35
28
 
36
- const n = 10000;
37
- const bigArray = range(n).map((x) => Math.floor(Math.random() * 100));
38
- const sortedBigArray = bigArray.slice().sort((a, b) => b - a);
29
+ // Range: indices 4 to 10 (2,7,3,6,4,5)
30
+ expect(topK(arr, 3, priorityAccessor, 4, 10)).toEqual([
31
+ { priority: 7 },
32
+ { priority: 6 },
33
+ { priority: 5 },
34
+ ]);
39
35
 
40
- for (let k = 0; k < 13000; k += 1000) {
41
- expect(topK(bigArray, k, priorityAccessor)).toEqual(
42
- sortedBigArray.slice(0, k)
43
- );
44
- }
36
+ // Range: indices 0 to 3 (0,9,1)
37
+ expect(topK(arr, 2, priorityAccessor, 0, 3)).toEqual([
38
+ { priority: 9 },
39
+ { priority: 1 },
40
+ ]);
45
41
  });
46
42
 
47
- test("topKSlice returns top k indexes in priority order", () => {
48
- expect(topKSlice([0, 1, 2], 3)).toEqual([2, 1, 0]);
49
- expect(topKSlice([1, 2, 3], 3)).toEqual([2, 1, 0]);
50
- expect(topKSlice([0, 1, 2], 1)).toEqual([2]);
51
- expect(topKSlice([0, 1, 2], 6)).toEqual([2, 1, 0]);
52
- expect(topKSlice([0, 1, 2, 3, 4, 5], 3)).toEqual([5, 4, 3]);
53
- expect(topKSlice([0, 9, 1, 8, 2, 7, 3, 6, 4, 5], 3)).toEqual([1, 3, 5]);
54
- expect(new Set(topKSlice([1, 1, 1, 2, 2, 2], 3))).toEqual(
55
- new Set([3, 4, 5])
56
- );
43
+ test("topK returns empty array if start >= end", () => {
44
+ const arr = [1, 2, 3, 4, 5];
45
+ expect(topK(arr, 3, (x) => x, 4, 4)).toEqual([]);
46
+ expect(topK(arr, 3, (x) => x, 5, 5)).toEqual([]);
47
+ expect(topK(arr, 3, (x) => x, 6, 6)).toEqual([]);
57
48
  });
58
49
 
59
- test("topKSlice returns top k indexes from a slice in priority order", () => {
60
- expect(topKSlice([0, 1, 2, 3, 4, 5], 2, 1, 5)).toEqual([4, 3]);
61
- expect(topKSlice([0, 9, 1, 8, 2, 7, 3, 6, 4, 5], 3, 1, 5)).toEqual([
62
- 1, 3, 4,
63
- ]);
50
+ test("topK works with negative and zero priorities in a range", () => {
51
+ const arr = [-10, 0, 5, -2, 3, 0, -1];
52
+ expect(topK(arr, 2, (x) => x, 1, 6)).toEqual([5, 3]);
53
+ expect(topK(arr, 3, (x) => x, 0, 4)).toEqual([5, 0, -2]);
64
54
  });
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "contributors": [],
9
9
  "license": "MIT",
10
- "version": "0.61.1",
10
+ "version": "0.62.1",
11
11
  "jsdelivr": "dist/bundle/index.js",
12
12
  "unpkg": "dist/bundle/index.js",
13
13
  "browser": "dist/bundle/index.js",
@@ -67,5 +67,5 @@
67
67
  "devDependencies": {
68
68
  "@types/long": "^4.0.1"
69
69
  },
70
- "gitHead": "092749ef334a80ee3434147f7bd2fdc09c0ede84"
70
+ "gitHead": "6224d0b26562632acb3f2b402362a8e2963246cc"
71
71
  }