UNPKG

@gmod/nclist

Version:

Read features from JBrowse 1 format nested containment list JSON

119 lines 4.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); //@ts-nocheck const quick_lru_1 = __importDefault(require("quick-lru")); const abortable_promise_cache_1 = __importDefault(require("@gmod/abortable-promise-cache")); const util_ts_1 = require("./util.js"); class NCList { constructor({ readFile, cacheSize = 100 }) { this.topList = []; this.chunkCache = new abortable_promise_cache_1.default({ cache: new quick_lru_1.default({ maxSize: cacheSize }), fill: this.readChunkItems.bind(this), }); this.readFile = readFile; if (!this.readFile) { throw new Error(`must provide a "readFile" function`); } } importExisting(nclist, attrs, baseURL, lazyUrlTemplate, lazyClass) { this.topList = nclist; this.attrs = attrs; this.start = attrs.makeFastGetter('Start'); this.end = attrs.makeFastGetter('End'); this.lazyClass = lazyClass; this.baseURL = baseURL; this.lazyUrlTemplate = lazyUrlTemplate; } binarySearch(arr, item, getter) { let low = -1; let high = arr.length; let mid; while (high - low > 1) { mid = (low + high) >>> 1; if (getter(arr[mid]) >= item) { high = mid; } else { low = mid; } } // if we're iterating rightward, return the high index; // if leftward, the low index if (getter === this.end) { return high; } return low; } readChunkItems(chunkNum) { const url = (0, util_ts_1.newURL)(this.lazyUrlTemplate.replaceAll(/\{Chunk\}/gi, chunkNum), this.baseURL); return (0, util_ts_1.readJSON)(url, this.readFile, { defaultContent: [] }); } async *iterateSublist(arr, from, to, inc, searchGet, testGet, path) { const getChunk = this.attrs.makeGetter('Chunk'); const getSublist = this.attrs.makeGetter('Sublist'); const pendingPromises = []; for (let i = this.binarySearch(arr, from, searchGet); i < arr.length && i >= 0 && inc * testGet(arr[i]) < inc * to; i += inc) { if (arr[i][0] === this.lazyClass) { // this is a lazily-loaded chunk of the nclist const chunkNum = getChunk(arr[i]); const chunkItemsP = this.chunkCache .get(chunkNum, chunkNum) .then(item => [item, chunkNum]); pendingPromises.push(chunkItemsP); } else { // this is just a regular feature yield [arr[i], path.concat(i)]; } // if this node has a contained sublist, process that too const sublist = getSublist(arr[i]); if (sublist) { yield* this.iterateSublist(sublist, from, to, inc, searchGet, testGet, path.concat(i)); } } for (const p of pendingPromises) { const [item, chunkNum] = await p; if (item) { yield* this.iterateSublist(item, from, to, inc, searchGet, testGet, [ ...path, chunkNum, ]); } } } async *iterate(from, to) { // calls the given function once for each of the // intervals that overlap the given interval // if from <= to, iterates left-to-right, otherwise iterates right-to-left // inc: iterate leftward or rightward const inc = from > to ? -1 : 1; // searchGet: search on start or end const searchGet = from > to ? this.start : this.end; // testGet: test on start or end const testGet = from > to ? this.end : this.start; if (this.topList.length > 0) { yield* this.iterateSublist(this.topList, from, to, inc, searchGet, testGet, [0]); } } async histogram(from, to, numBins) { // calls callback with a histogram of the feature density // in the given interval const result = new Array(numBins); result.fill(0); const binWidth = (to - from) / numBins; for await (const feat of this.iterate(from, to)) { const firstBin = Math.max(0, ((this.start(feat) - from) / binWidth) | 0); const lastBin = Math.min(numBins, ((this.end(feat) - from) / binWidth) | 0); for (let bin = firstBin; bin <= lastBin; bin += 1) { result[bin] += 1; } } return result; } } exports.default = NCList; //# sourceMappingURL=nclist.js.map