Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
jokester committed Apr 20, 2024
1 parent 9d86895 commit b69c516
Show file tree
Hide file tree
Showing 18 changed files with 70 additions and 380 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@

---

Common TypeScript code I used in multiple app.
Simply and generic TypeScript utils.

![Check](https://github.com/jokester/ts-commonutil/workflows/Check/badge.svg)
[![codecov](https://codecov.io/gh/jokester/ts-commonutil/graph/badge.svg?token=95f53H027x)](https://codecov.io/gh/jokester/ts-commonutil)
[![npm version](https://badge.fury.io/js/%40jokester%2Fts-commonutil.svg)](https://badge.fury.io/js/%40jokester%2Fts-commonutil)

## How to Use

```
yarn add @jokester/ts-commonutil
install `@jokester/ts-commonutil` from NPM.

```
## Not included but my go-to libraries

- LRU: [lru-cache](https://www.npmjs.com/package/lru-cache)
- fp: [fp-ts](https://www.npmjs.com/package/fp-ts)
- React hooks
- [foxact](https://foxact.skk.moe/)

## Content

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions src/collection/default-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,19 @@ export class DefaultMap<K, V> extends Map<K, V> {
return this.get(k)!;
}
}

export class WeakDefaultMap<K extends object, V> extends WeakMap<K, V> {
constructor(
private readonly createDefault: (k: K) => V,
entries?: readonly [K, V][],
) {
super(entries);
}

getOrCreate(k: K): V {
if (!this.has(k)) {
this.set(k, this.createDefault(k));
}
return this.get(k)!;
}
}
8 changes: 8 additions & 0 deletions src/collection/iterables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,11 @@ export const Iterables = {

type GeneralIterable<T> = Iterable<T> | AsyncIterable<T>;
type MaybePromise<T> = T | PromiseLike<T>;

export function toMap<T, K>(items: Iterable<T>, keyer: (t: T) => K): Map<K, T> {
const ret = new Map<K, T>();
for (const i of items) {
ret.set(keyer(i), i);
}
return ret;
}
11 changes: 0 additions & 11 deletions src/collection/maps.ts

This file was deleted.

20 changes: 10 additions & 10 deletions src/collection/min-heap.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { MinHeap } from './min-heap';
import { NumericOrder } from '../algebra/total-ordered';
import * as fpts from 'fp-ts';

describe('MinHeap', () => {
it('insert elements', () => {
const testee = new MinHeap(NumericOrder);
const testee = new MinHeap(fpts.number.Ord);
expect(testee.slice()).toEqual([]);

expect(testee.insert(5).slice()).toEqual([5]);
Expand All @@ -16,7 +16,7 @@ describe('MinHeap', () => {
});

it('removes element', () => {
const testee = new MinHeap(NumericOrder).insertMany(5, 2, 1, 6, 0);
const testee = new MinHeap(fpts.number.Ord).insertMany(5, 2, 1, 6, 0);

expect(testee.remove()).toEqual(0);
expect(testee.slice()).toEqual([1, 5, 2, 6]);
Expand All @@ -30,15 +30,15 @@ describe('MinHeap', () => {
});

it('removes element - 2', () => {
const testee = new MinHeap(NumericOrder).insert(0).insert(2).insert(3).insert(100).insert(200).insert(4);
const testee = new MinHeap(fpts.number.Ord).insert(0).insert(2).insert(3).insert(100).insert(200).insert(4);

expect(testee.slice()).toEqual([0, 2, 3, 100, 200, 4]);
expect(testee.remove()).toEqual(0);
expect(testee.slice()).toEqual([2, 4, 3, 100, 200]);
});

it('throws when remove from or peek an empty && strict heap', () => {
const testee = new MinHeap(NumericOrder, true);
const testee = new MinHeap(fpts.number.Ord, true);
expect(testee.slice()).toEqual([]);

expect(() => testee.remove()).toThrow(/nothing to remove/);
Expand All @@ -47,25 +47,25 @@ describe('MinHeap', () => {
});

it('can be initialized with initialTree', () => {
const testee = new MinHeap(NumericOrder, false, /* illegal tree */ [1, 0]);
const testee = new MinHeap(fpts.number.Ord, false, /* illegal tree */ [1, 0]);

expect(() => new MinHeap(NumericOrder, true, [1, 0])).toThrow(/assertInvariants/);
expect(() => new MinHeap(fpts.number.Ord, true, [1, 0])).toThrow(/assertInvariants/);
});

it('can be cloned', () => {
const testee = new MinHeap(NumericOrder, false, /* illegal tree */ [1, 0]);
const testee = new MinHeap(fpts.number.Ord, false, /* illegal tree */ [1, 0]);

expect(testee.clone().slice()).toEqual([1, 0]);
});

it('can be shrinked', () => {
const testee = new MinHeap(NumericOrder).insertMany(5, 4, 3, 2, 1, -1);
const testee = new MinHeap(fpts.number.Ord).insertMany(5, 4, 3, 2, 1, -1);

expect(testee.shrink(3).removeMany(3)).toEqual([-1, 1, 2]);
});

it('can shrink to a given upperlimit', () => {
const testee = new MinHeap(NumericOrder).insertMany(5, 4, 3, 2, 1, -1);
const testee = new MinHeap(fpts.number.Ord).insertMany(5, 4, 3, 2, 1, -1);

expect(testee.clone().shrinkUntil(4).removeMany(100)).toEqual([-1, 1, 2, 3]);

Expand Down
20 changes: 12 additions & 8 deletions src/collection/min-heap.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { TotalOrdered } from '../algebra/total-ordered';
import { positions } from './btree';
import { Ord } from 'fp-ts/Ord';

function isBefore<T>(ord: Ord<T>, a: T, b: T): boolean {
return ord.compare(a, b) < 0;
}

export class MinHeap<T> {
private readonly tree: T[] = [];
slice = this.tree.slice.bind(this.tree);

constructor(
private readonly order: TotalOrdered<T>,
private readonly order: Ord<T>,
private readonly strict = false,
initialTree?: T[],
) {
Expand All @@ -27,7 +31,7 @@ export class MinHeap<T> {
j = positions.parent(i);
this.tree[i] = value;

while (i && this.order.before(value /* i.e. this.tree[i] */, this.tree[j])) {
while (i && isBefore(this.order, value /* i.e. this.tree[i] */, this.tree[j])) {
this.tree[i] = this.tree[j];
this.tree[j] = value;

Expand Down Expand Up @@ -61,20 +65,20 @@ export class MinHeap<T> {
*/
if (r < this.tree.length) {
// when it has 2 children
if (this.order.before(this.tree[l], this.tree[r]) && this.order.before(this.tree[l], v)) {
if (isBefore(this.order, this.tree[l], this.tree[r]) && isBefore(this.order, this.tree[l], v)) {
// swap [i] and [l] and continue
this.tree[i] = this.tree[l];
this.tree[l] = v;
i = l;
} else if (this.order.before(this.tree[r], this.tree[l]) && this.order.before(this.tree[r], v)) {
} else if (isBefore(this.order, this.tree[r], this.tree[l]) && isBefore(this.order, this.tree[r], v)) {
// swap [i] and [l] and continue
this.tree[i] = this.tree[r];
this.tree[r] = v;
i = r;
} else {
break; // v is already before all children
}
} else if (l < this.tree.length && this.order.before(this.tree[l], v)) {
} else if (l < this.tree.length && isBefore(this.order, this.tree[l], v)) {
this.tree[i] = this.tree[l];
this.tree[l] = v;
break; // there cannot be next level
Expand Down Expand Up @@ -119,7 +123,7 @@ export class MinHeap<T> {
const afterShrink: T[] = [];
while (
this.tree.length &&
(this.order.before(this.tree[0], v) || (inclusive && this.order.equal(this.tree[0], v)))
(isBefore(this.order, this.tree[0], v) || (inclusive && !this.order.compare(this.tree[0], v)))
) {
afterShrink.push(this.remove()!);
}
Expand All @@ -130,7 +134,7 @@ export class MinHeap<T> {
private assertInvariants() {
for (let i = 1; i < this.tree.length; i++) {
const p = positions.parent(i);
if (!this.order.before(this.tree[p], this.tree[i])) {
if (!this.order.compare(this.tree[p], this.tree[i])) {
throw new Error(`MinHeap#assertInvariants(): expected this.tree[${p} to be ordered before this.tree[${i}]`);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/collection/multiset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Multiset } from './multiset';

describe(Multiset, () => {
it('keeps elements and count', () => {
const testee = new Multiset<string>();
const testee = new Multiset<string>(false);

testee.setCount('abc', 1);
testee.setCount('abc', 2);
Expand All @@ -16,7 +16,7 @@ describe(Multiset, () => {
testee.setCount('abc', 0);
testee.setCount('abc', 1);
testee.setCount('abc', 1);
testee.setCount('abc', 0, false);
testee.setCount('abc', 0);
expect(testee.maxCount()).toEqual(1);
expect(testee.getCount('abc')).toEqual(0);
expect(testee.getCount('abd')).toEqual(1);
Expand Down
6 changes: 4 additions & 2 deletions src/collection/multiset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ export class Multiset<T> {
private map = new DefaultMap</* count */ number, /* objects */ Set<T>>((k) => new Set());
private countMap = new Map</* object*/ T, /* count */ number>();

setCount(obj: T, count: number, removeOnZeroFreq = true): void {
constructor(readonly removeOnZeroFreq = true) {}

setCount(obj: T, count: number): void {
const existedCount = this.countMap.get(obj);

if (existedCount === count) {
Expand All @@ -16,7 +18,7 @@ export class Multiset<T> {

const existedSet = this.map.get(existedCount)!;
existedSet.delete(obj);
if (!existedSet.size && removeOnZeroFreq) {
if (!existedSet.size && this.removeOnZeroFreq) {
this.map.delete(existedCount);
}
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/collection/segment-tree.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Monoid } from '../algebra/monoid';
import { Monoid } from 'fp-ts/lib/Monoid';
import { positions, powOf2 } from './btree';

/**
Expand Down Expand Up @@ -35,7 +35,7 @@ export class SegmentTree<T> {
const sums: T[] = (this.sums = []);

for (let i = 0; i < lenSums; i++) {
sums[i] = this.monoid.id;
sums[i] = this.monoid.empty;
}

// FIXME: set
Expand Down
10 changes: 5 additions & 5 deletions src/stress/orderBy.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { orderBy } from './orderBy';
import { sortBy } from './sortBy';

describe('orderBy', () => {
it('sorts value DESC by "natural" JS ordering', () => {
expect(orderBy([2, 3, 1], (v) => v)).toEqual([3, 2, 1]);
expect(orderBy([1, 1, 2], (v) => v)).toEqual([2, 1, 1]);
expect(sortBy([2, 3, 1], (v) => v)).toEqual([3, 2, 1]);
expect(sortBy([1, 1, 2], (v) => v)).toEqual([2, 1, 1]);
});
it('sorts value ASC by "natural" JS ordering', () => {
expect(orderBy([2, 3, 1], (v) => v, true)).toEqual([1, 2, 3]);
expect(orderBy([1, 1, 2], (v) => v, true)).toEqual([1, 1, 2]);
expect(sortBy([2, 3, 1], (v) => v, true)).toEqual([1, 2, 3]);
expect(sortBy([1, 1, 2], (v) => v, true)).toEqual([1, 1, 2]);
});
});
6 changes: 3 additions & 3 deletions src/stress/orderBy.ts → src/stress/sortBy.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* (for more complicated case, consider fp-ts Order<T> typeclass)
* (for more complicated case, consider fp-ts Ord<T> typeclass)
* @param values
* @param key
* @param key (if it should be cached, caller should use a cached impl)
* @param asc
*/
export function orderBy<T, O>(values: T[], key: (v: T) => O, asc = false): T[] {
export function sortBy<T, O>(values: T[], key: (v: T) => O, asc = true): T[] {
const indexes = values.map((v, i) => ({ v, i }));
return indexes.sort((a, b) => (asc ? compare(a.v, b.v) : -compare(a.v, b.v))).map((_) => _.v);
}
Expand Down
Loading

0 comments on commit b69c516

Please sign in to comment.