mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-15 20:57:56 +08:00
use quicksort instead of default system sort (#413)
This commit is contained in:
56
client/__tests__/util/typedCrossfilter/sort.test.js
Normal file
56
client/__tests__/util/typedCrossfilter/sort.test.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { sort, sortIndex } from "../../../src/util/typedCrossfilter/sort";
|
||||
|
||||
function fillRange(arr, start = 0) {
|
||||
const larr = arr;
|
||||
for (let i = 0, len = larr.length; i < len; i += 1) {
|
||||
larr[i] = i + start;
|
||||
}
|
||||
return larr;
|
||||
}
|
||||
|
||||
function fillRand(arr) {
|
||||
for (let i = 0, len = arr.length; i < len; i += 1) {
|
||||
arr[i] = Math.random();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
describe("sort", () => {
|
||||
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type =>
|
||||
test(Type.name, () => {
|
||||
expect(sort(Type.from([6, 5, 4, 3, 2, 1, 0]))).toMatchObject(
|
||||
Type.from([0, 1, 2, 3, 4, 5, 6])
|
||||
);
|
||||
expect(sort(Type.from([6, 5, 4, 3, 2, 1]))).toMatchObject(
|
||||
Type.from([1, 2, 3, 4, 5, 6])
|
||||
);
|
||||
|
||||
const source = fillRand(new Type(1000));
|
||||
expect(sort(Type.from(source))).toMatchObject(Type.from(source).sort());
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe("sortIndex", () => {
|
||||
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type =>
|
||||
test(Type.name, () => {
|
||||
const source1 = Type.from([6, 5, 4, 3, 2, 1, 0]);
|
||||
const index1 = fillRange(new Uint32Array(source1.length));
|
||||
expect(sortIndex(index1, source1)).toMatchObject(
|
||||
index1.sort((a, b) => source1[a] - source1[b])
|
||||
);
|
||||
|
||||
const source2 = Type.from([6, 5, 4, 3, 2, 1]);
|
||||
const index2 = fillRange(new Uint32Array(source2.length));
|
||||
expect(sortIndex(index2, source2)).toMatchObject(
|
||||
index2.sort((a, b) => source1[a] - source1[b])
|
||||
);
|
||||
|
||||
const source3 = fillRand(new Type(1000));
|
||||
const index3 = fillRange(new Uint32Array(source3.length));
|
||||
expect(sortIndex(index3, source3)).toMatchObject(
|
||||
index3.sort((a, b) => source1[a] - source1[b])
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
100
client/src/util/typedCrossfilter/sort.js
Normal file
100
client/src/util/typedCrossfilter/sort.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const SmallArray = 32;
|
||||
|
||||
function insertionsort(a, lo, hi) {
|
||||
for (let i = lo + 1; i < hi + 1; i += 1) {
|
||||
const x = a[i];
|
||||
let j;
|
||||
for (j = i; j > lo && a[j - 1] > x; j -= 1) {
|
||||
a[j] = a[j - 1];
|
||||
}
|
||||
a[j] = x;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function insertionsortIndirect(a, s, lo, hi) {
|
||||
for (let i = lo + 1; i < hi + 1; i += 1) {
|
||||
const x = a[i];
|
||||
const t = s[x];
|
||||
let j;
|
||||
for (j = i; j > lo && s[a[j - 1]] > t; j -= 1) {
|
||||
a[j] = a[j - 1];
|
||||
}
|
||||
a[j] = x;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function quicksort(a, lo, hi) {
|
||||
if (hi - lo < SmallArray) {
|
||||
return insertionsort(a, lo, hi);
|
||||
}
|
||||
if (lo < hi) {
|
||||
// partition
|
||||
const mid = Math.floor((lo + hi) / 2);
|
||||
const p = a[mid];
|
||||
let i = lo - 1;
|
||||
let j = hi + 1;
|
||||
while (i < j) {
|
||||
do {
|
||||
i += 1;
|
||||
} while (a[i] < p);
|
||||
do {
|
||||
j -= 1;
|
||||
} while (a[j] > p);
|
||||
if (i < j) {
|
||||
const tmp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = tmp;
|
||||
}
|
||||
}
|
||||
// sort
|
||||
quicksort(a, lo, j);
|
||||
quicksort(a, j + 1, hi);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function quicksortIndirect(a, s, lo, hi) {
|
||||
if (hi - lo < SmallArray) {
|
||||
return insertionsortIndirect(a, s, lo, hi);
|
||||
}
|
||||
if (lo < hi) {
|
||||
// partition
|
||||
const mid = Math.floor((lo + hi) / 2);
|
||||
const p = a[mid];
|
||||
const t = s[p];
|
||||
let i = lo - 1;
|
||||
let j = hi + 1;
|
||||
while (i < j) {
|
||||
do {
|
||||
i += 1;
|
||||
} while (s[a[i]] < t);
|
||||
do {
|
||||
j -= 1;
|
||||
} while (s[a[j]] > t);
|
||||
if (i < j) {
|
||||
const tmp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = tmp;
|
||||
}
|
||||
}
|
||||
// sort
|
||||
quicksortIndirect(a, s, lo, j);
|
||||
quicksortIndirect(a, s, j + 1, hi);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// Convenience wrappers
|
||||
export function sort(arr, comparator = undefined) {
|
||||
if (comparator !== undefined) {
|
||||
// XXX for now
|
||||
return arr.sort(arr, comparator);
|
||||
}
|
||||
return quicksort(arr, 0, arr.length - 1);
|
||||
}
|
||||
|
||||
export function sortIndex(index, source) {
|
||||
return quicksortIndirect(index, source, 0, index.length - 1);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// jshint esversion: 6
|
||||
/* eslint no-bitwise: "off" */
|
||||
|
||||
import { sortIndex } from "./sort";
|
||||
|
||||
/*
|
||||
Utility functions, private to this module.
|
||||
*/
|
||||
@@ -31,7 +33,7 @@ export function sliceByIndex(src, index) {
|
||||
|
||||
export function makeSortIndex(src) {
|
||||
const index = fillRange(new Uint32Array(src.length));
|
||||
index.sort((a, b) => src[a] - src[b]);
|
||||
sortIndex(index, src);
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user