Skip to content

Commit

Permalink
feat(associative): update SortedSet, IEquivSet, add tests
Browse files Browse the repository at this point in the history
- remove opts() from IEquivSet
- add min/max key query opts for SortedSet.entries/keys/values()
  • Loading branch information
postspectacular committed Mar 20, 2019
1 parent 3feb3ce commit e8234e8
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 24 deletions.
14 changes: 7 additions & 7 deletions packages/associative/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ export interface IEquivSet<T>
disj(xs: Iterable<T>): this;
get(val: T, notFound?: any): any;
first(): T;
opts(): EquivSetOpts<T>;
}

export interface EquivSetConstructor {
new (): IEquivSet<any>;
new <T>(values?: Iterable<T>, opts?: EquivSetOpts<T>): IEquivSet<T>;
new <T>(values?: Iterable<T>, opts?: any): IEquivSet<T>;
readonly prototype: IEquivSet<any>;
}

Expand All @@ -42,15 +41,16 @@ export interface EquivMapOpts<K> extends EquivSetOpts<K> {
/**
* SortedMapOpts implementation config settings.
*/
export interface SortedMapOpts<K> extends EquivSetOpts<K> {
export interface SortedMapOpts<K> {
/**
* Key comparison function. Must follow standard comparator contract
* and return:
* - negative if `a < b`
* - positive if `a > b`
* - `0` if `a == b`
*
* Note: The `SortedMap` implementation only uses `<` comparisons.
* Note: The `SortedMap` implementation only uses `<` and `==` style
* comparisons.
*
* Default: `@thi.ng/compare`
*/
Expand All @@ -59,12 +59,12 @@ export interface SortedMapOpts<K> extends EquivSetOpts<K> {
* Initial capacity before resizing (doubling) occurs.
* This value will be rounded up to next pow2.
*
* Default: 16
* Default: 8
*/
capacity: number;
/**
* Probability for a value to exist in any express lane.
* Default: `1 / Math.E`
* Probability for a value to exist in any express lane of the
* underlying Skip List implementation. Default: `1 / Math.E`
*/
probability: number;
}
Expand Down
18 changes: 7 additions & 11 deletions packages/associative/src/sorted-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ import {
Fn3,
IObjectOf,
Pair,
Predicate2,
SEMAPHORE
} from "@thi.ng/api";
import { compare } from "@thi.ng/compare";
import { equiv } from "@thi.ng/equiv";
import { isReduced, map, ReductionFn } from "@thi.ng/transducers";
import { SortedMapOpts } from "./api";

interface Skip3State<K, V> {
interface SortedMapState<K, V> {
head: Node<K, V>;
cmp: Comparator<K>;
equiv: Predicate2<K>;
maxh: number;
h: number;
length: number;
Expand All @@ -36,7 +34,7 @@ class Node<K, V> {

// stores private properties for all instances
// http://fitzgeraldnick.com/2014/01/13/hiding-implementation-details-with-e6-weakmaps.html
const __private = new WeakMap<SortedMap<any, any>, Skip3State<any, any>>();
const __private = new WeakMap<SortedMap<any, any>, SortedMapState<any, any>>();

export class SortedMap<K, V> extends Map<K, V> {
static fromObject<T>(
Expand All @@ -57,7 +55,7 @@ export class SortedMap<K, V> extends Map<K, V> {
static DEFAULT_P = 1 / Math.E;

constructor(
pairs: Iterable<Pair<K, V>>,
pairs?: Iterable<Pair<K, V>>,
opts: Partial<SortedMapOpts<K>> = {}
) {
super();
Expand All @@ -67,7 +65,6 @@ export class SortedMap<K, V> extends Map<K, V> {
head: new Node<K, V>(null, null, 0),
cap: Math.pow(2, maxh),
cmp: opts.compare || compare,
equiv: opts.equiv || equiv,
p: opts.probability || SortedMap.DEFAULT_P,
maxh,
length: 0,
Expand Down Expand Up @@ -110,12 +107,12 @@ export class SortedMap<K, V> extends Map<K, V> {
}
}

keys(key?: K): IterableIterator<K> {
return map((p) => p[0], this.entries(key));
keys(key?: K, max = false): IterableIterator<K> {
return map((p) => p[0], this.entries(key, max));
}

values(key?: K): IterableIterator<V> {
return map((p) => p[1], this.entries(key));
values(key?: K, max = false): IterableIterator<V> {
return map((p) => p[1], this.entries(key, max));
}

get size() {
Expand Down Expand Up @@ -293,7 +290,6 @@ export class SortedMap<K, V> extends Map<K, V> {
return {
capacity: $this.cap,
compare: $this.cmp,
equiv: $this.equiv,
probability: $this.p
};
}
Expand Down
12 changes: 6 additions & 6 deletions packages/associative/src/sorted-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,16 @@ export class SortedSet<T> extends Set<T>
return __private.get(this).$reduce((_acc, x) => rfn(_acc, x[0]), acc);
}

entries(): IterableIterator<Pair<T, T>> {
return __private.get(this).entries();
entries(key?: T, max = false): IterableIterator<Pair<T, T>> {
return __private.get(this).entries(key, max);
}

keys(): IterableIterator<T> {
return __private.get(this).keys();
keys(key?: T, max = false): IterableIterator<T> {
return __private.get(this).keys(key, max);
}

values(): IterableIterator<T> {
return __private.get(this).values();
values(key?: T, max = false): IterableIterator<T> {
return __private.get(this).values(key, max);
}

add(value: T) {
Expand Down
50 changes: 50 additions & 0 deletions packages/associative/test/sorted-map.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { shuffle } from "@thi.ng/arrays";
import { equiv } from "@thi.ng/equiv";
import { range, repeat, zip } from "@thi.ng/transducers";
import * as assert from "assert";
import { SortedMap } from "../src/sorted-map";

Expand All @@ -20,6 +23,28 @@ describe("SortedMap", () => {
assert.equal(m.size, 3);
});

it("clear", () => {
m.clear();
assert.equal(m.size, 0);
assert.deepEqual([...m.entries()], []);
});

it("empty", () => {
const m2 = m.empty();
assert.equal(m.size, 3);
assert.equal(m2.size, 0);
assert.deepEqual([...m2.entries()], []);
});

it("copy", () => {
assert.deepEqual(m.copy(), m);
});

it("equiv", () => {
assert.ok(equiv(m.copy(), m));
assert.ok(!equiv(m, new SortedMap<any, any>()));
});

it("has", () => {
assert(m.has("a"));
assert(m.has("b"));
Expand All @@ -29,6 +54,12 @@ describe("SortedMap", () => {
assert(!m.has("@"));
});

it("first", () => {
assert.deepEqual(["a", 1], m.first());
m.set("A", 10);
assert.deepEqual(["A", 10], m.first());
});

it("get", () => {
assert.strictEqual(m.get("a"), 1);
assert.strictEqual(m.get("b"), 2);
Expand Down Expand Up @@ -107,4 +138,23 @@ describe("SortedMap", () => {
m.set("d", 0);
assert.deepEqual([...m.values()], [1, 0, 2, 3, 0]);
});

it("comparator", () => {
m = SortedMap.fromObject(
{ a: 1, b: 2, c: 3 },
{
compare: (a: string, b: string) =>
a === b ? 0 : a < b ? 1 : -1
}
);
assert.deepEqual([["c", 3], ["b", 2], ["a", 1]], [...m.entries()]);
});

it("fuzz", () => {
const keys = [...range(32)];
for (let i = 0; i < 1000; i++) {
m = new SortedMap(zip(shuffle(keys.slice()), repeat(1)));
assert.deepEqual([...m.keys()], keys);
}
});
});

0 comments on commit e8234e8

Please sign in to comment.