buckets.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. type Bucket = {
  7. key: any,
  8. count: number,
  9. offset: number
  10. }
  11. function _makeBuckets(indices: Helpers.ArrayLike<number>, getKey: (i: number) => any, sort: boolean, start: number, end: number) {
  12. const buckets = new Map<any, Bucket>();
  13. const bucketList: Bucket[] = [];
  14. let prevKey = getKey(indices[0]);
  15. let isBucketed = true;
  16. for (let i = start; i < end; i++) {
  17. const key = getKey(indices[i]);
  18. if (buckets.has(key)) {
  19. buckets.get(key)!.count++;
  20. if (prevKey !== key) isBucketed = false;
  21. } else {
  22. const bucket: Bucket = { key, count: 1, offset: i };
  23. buckets.set(key, bucket);
  24. bucketList[bucketList.length] = bucket;
  25. }
  26. prevKey = key;
  27. }
  28. const bucketOffsets = new Int32Array(bucketList.length + 1);
  29. bucketOffsets[bucketList.length] = end;
  30. let sorted = true;
  31. if (sort) {
  32. for (let i = 1, _i = bucketList.length; i < _i; i++) {
  33. if (bucketList[i - 1].key > bucketList[i].key) {
  34. sorted = false;
  35. break;
  36. }
  37. }
  38. }
  39. if (isBucketed && sorted) {
  40. for (let i = 0; i < bucketList.length; i++) bucketOffsets[i] = bucketList[i].offset;
  41. return bucketOffsets;
  42. }
  43. if (sort && !sorted) {
  44. bucketList.sort((x, y) => x.key <= y.key ? -1 : 1);
  45. }
  46. let offset = 0;
  47. for (let i = 0; i < bucketList.length; i++) {
  48. const b = bucketList[i];
  49. b.offset = offset;
  50. offset += b.count;
  51. }
  52. const reorderedIndices = new Int32Array(end - start);
  53. for (let i = start; i < end; i++) {
  54. const key = getKey(indices[i]);
  55. const bucket = buckets.get(key)!;
  56. reorderedIndices[bucket.offset++] = indices[i];
  57. }
  58. for (let i = 0, _i = reorderedIndices.length; i < _i; i++) {
  59. indices[i + start] = reorderedIndices[i];
  60. }
  61. bucketOffsets[0] = start;
  62. for (let i = 1; i < bucketList.length; i++) bucketOffsets[i] = bucketList[i - 1].offset + start;
  63. return bucketOffsets;
  64. }
  65. /**
  66. * Reorders indices so that the same keys are next to each other, [start, end)
  67. * Returns the offsets of buckets. So that [offsets[i], offsets[i + 1]) determines the range.
  68. */
  69. export function makeBuckets<T>(indices: Helpers.ArrayLike<number>, getKey: (i: number) => string | number, sort: boolean, start?: number, end?: number): ArrayLike<number> {
  70. const s = start || 0;
  71. const e = typeof end === 'undefined' ? indices.length : end;
  72. if (e - s <= 0) throw new Error('Can only bucket non-empty collections.');
  73. return _makeBuckets(indices, getKey, sort, s, e);
  74. }