Skip to content

Commit

Permalink
feat(associative): #210, add defXXX factory fns
Browse files Browse the repository at this point in the history
- add defArraySet()
- add defLLSet()
- add defSortedSet()
- add defSparseSet()
- add defEquivMap()
- add defHashMap()
- add defSortedMap()

BREAKING CHANGE: remove static `fromObject()` map factories

- merged with defHashMap(), defSortedMap()
  • Loading branch information
postspectacular committed Mar 28, 2020
1 parent 115651f commit 48ae24a
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 67 deletions.
6 changes: 6 additions & 0 deletions packages/associative/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export interface EquivSetOpts<T> {
}

export interface EquivMapOpts<K> extends EquivSetOpts<K> {
/**
* Underlying {@link IEquivSet} implementation for storing the
* unique keys of the map.
*
* @defaultValue {@link ArraySet}
*/
keys: EquivSetConstructor<K>;
}

Expand Down
10 changes: 8 additions & 2 deletions packages/associative/src/array-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ const __vals = (inst: ArraySet<any>) => __private.get(inst)!.vals;
* and by the default uses {@link @thi.ng/equiv#equiv} for equivalence
* checking.
*
* Additionally, the type also implements the {@link @thi.ng/api#ICopy}, {@link @thi.ng/api#IEmpty} and
* {@link @thi.ng/api#IEquiv} interfaces itself.
* Additionally, the type also implements the {@link @thi.ng/api#ICopy},
* {@link @thi.ng/api#IEmpty} and {@link @thi.ng/api#IEquiv} interfaces
* itself.
*/
@inspectable
export class ArraySet<T> extends Set<T> implements IEquivSet<T> {
Expand Down Expand Up @@ -153,3 +154,8 @@ export class ArraySet<T> extends Set<T> implements IEquivSet<T> {
return { equiv: __private.get(this)!.equiv };
}
}

export const defArraySet = <T>(
vals?: Iterable<T> | null,
opts?: Partial<EquivSetOpts<T>>
) => new ArraySet(vals, opts);
42 changes: 20 additions & 22 deletions packages/associative/src/equiv-map.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SEMAPHORE } from "@thi.ng/api";
import { isPlainObject } from "@thi.ng/checks";
import { equiv } from "@thi.ng/equiv";
import { pairs } from "@thi.ng/transducers";
import { ArraySet } from "./array-set";
import { dissoc } from "./dissoc";
import { equivMap } from "./internal/equiv";
Expand All @@ -25,28 +27,6 @@ export class EquivMap<K, V> extends Map<K, V>
ICopy<EquivMap<K, V>>,
IEmpty<EquivMap<K, V>>,
IEquiv {
/**
* Converts given vanilla object into an {@link EquivMap} instance with
* default (or optionally provided) options and returns it. By
* default uses strict `===` equality check for `equiv` option.
*
* @param obj - source object
* @param opts - config options
*/
static fromObject<T>(
obj: IObjectOf<T>,
opts?: Partial<EquivMapOpts<string>>
): EquivMap<string, T> {
const m = new EquivMap<string, T>(null, {
equiv: (a, b) => a === b,
...opts,
});
for (let k in obj) {
obj.hasOwnProperty(k) && m.set(k, obj[k]);
}
return m;
}

/**
* Creates a new instance with optional initial key-value pairs and
* provided options. If no `opts` are given, uses `ArraySet` for
Expand Down Expand Up @@ -179,3 +159,21 @@ export class EquivMap<K, V> extends Map<K, V>
return __private.get(this)!.opts;
}
}

export function defEquivMap<K, V>(
pairs?: Iterable<Pair<K, V>> | null,
opts?: Partial<EquivMapOpts<K>>
): EquivMap<K, V>;
export function defEquivMap<V>(
obj: IObjectOf<V>,
opts?: Partial<EquivMapOpts<string>>
): EquivMap<string, V>;
export function defEquivMap<V>(
src: any,
opts?: Partial<EquivMapOpts<any>>
): EquivMap<any, V> {
return new EquivMap(
isPlainObject(src) ? pairs(<IObjectOf<V>>src) : src,
opts
);
}
32 changes: 31 additions & 1 deletion packages/associative/src/hash-map.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ceilPow2 } from "@thi.ng/binary";
import { isPlainObject } from "@thi.ng/checks";
import { equiv } from "@thi.ng/equiv";
import { map } from "@thi.ng/transducers";
import { dissoc } from "./dissoc";
import { equivMap } from "./internal/equiv";
import { inspectable } from "./internal/inspect";
Expand All @@ -12,6 +14,7 @@ import type {
IEquiv,
Pair,
Predicate2,
IObjectOf,
} from "@thi.ng/api";
import type { HashMapOpts } from "./api";

Expand All @@ -34,6 +37,7 @@ const __iterator = <K, V>(map: HashMap<K, V>, id: 0 | 1) =>
};

const DEFAULT_CAP = 16;
const DEFAULT_LOAD = 0.75;

/**
* Configurable hash map implementation w/ ES6 Map API. Uses open
Expand Down Expand Up @@ -68,7 +72,7 @@ export class HashMap<K, V> extends Map<K, V>
__private.set(this, {
hash: opts.hash,
equiv: opts.equiv || equiv,
load: opts.load || 0.75,
load: opts.load || DEFAULT_LOAD,
mask: m,
bins: new Array(m + 1),
size: 0,
Expand Down Expand Up @@ -235,3 +239,29 @@ export class HashMap<K, V> extends Map<K, V>
}
}
}

export function defHashMap<K, V>(
pairs: Iterable<Pair<K, V>> | null,
opts: HashMapOpts<K>
): HashMap<K, V>;
export function defHashMap<V>(
obj: IObjectOf<V>,
opts: HashMapOpts<string>
): HashMap<string, V>;
export function defHashMap<V>(
src: any,
opts: HashMapOpts<any>
): HashMap<any, V> {
if (isPlainObject(src)) {
const keys = Object.keys(src);
return new HashMap<string, V>(
map((k) => <Pair<string, V>>[k, (<IObjectOf<V>>src)[k]], keys),
{
cap: keys.length / (opts.load || DEFAULT_LOAD),
...opts,
}
);
} else {
return new HashMap(src, opts);
}
}
20 changes: 13 additions & 7 deletions packages/associative/src/ll-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ const __private = new WeakMap<LLSet<any>, SetProps<any>>();
const __vals = (inst: LLSet<any>) => __private.get(inst)!.vals;

/**
* Similar to {@link ArraySet}, this class is an alternative implementation of
* the native ES6 Set API using a {@link @thi.ng/dcons#DCons} linked
* list as backing store and a customizable value equality / equivalence
* predicate. By the default uses {@link @thi.ng/equiv#equiv} for
* equivalence checking.
* Similar to {@link ArraySet}, this class is an alternative
* implementation of the native ES6 Set API using a
* {@link @thi.ng/dcons#DCons} linked list as backing store and a
* customizable value equality / equivalence predicate. By the default
* uses {@link @thi.ng/equiv#equiv} for equivalence checking.
*
* Additionally, the type also implements the {@link @thi.ng/api#ICopy}, {@link @thi.ng/api#IEmpty} and
* {@link @thi.ng/api#IEquiv} interfaces itself.
* Additionally, the type also implements the {@link @thi.ng/api#ICopy},
* {@link @thi.ng/api#IEmpty} and {@link @thi.ng/api#IEquiv} interfaces
* itself.
*/
@inspectable
export class LLSet<T> extends Set<T> implements IEquivSet<T> {
Expand Down Expand Up @@ -159,3 +160,8 @@ export class LLSet<T> extends Set<T> implements IEquivSet<T> {
return { equiv: __private.get(this)!.equiv };
}
}

export const defLLSet = <T>(
vals?: Iterable<T> | null,
opts?: Partial<EquivSetOpts<T>>
) => new LLSet(vals, opts);
48 changes: 27 additions & 21 deletions packages/associative/src/sorted-map.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SEMAPHORE } from "@thi.ng/api";
import { isPlainObject } from "@thi.ng/checks";
import { compare } from "@thi.ng/compare";
import { isReduced, map } from "@thi.ng/transducers";
import { dissoc } from "./dissoc";
Expand Down Expand Up @@ -37,27 +38,6 @@ const __private = new WeakMap<SortedMap<any, any>, SortedMapState<any, any>>();

@inspectable
export class SortedMap<K, V> extends Map<K, V> {
/**
* Creates new {@link SortedMap} instance from given object's key-value
* pairs.
*
* @param obj - source object
* @param opts - config options
*/
static fromObject<T>(
obj: IObjectOf<T>,
opts?: Partial<SortedMapOpts<string>>
): SortedMap<string, T> {
const m = new SortedMap<string, T>(null, {
capacity: Object.keys(obj).length,
...opts,
});
for (let k in obj) {
obj.hasOwnProperty(k) && m.set(k, obj[k]);
}
return m;
}

static DEFAULT_CAP = 8;
static DEFAULT_P = 1 / Math.E;

Expand Down Expand Up @@ -311,3 +291,29 @@ export class SortedMap<K, V> extends Map<K, V> {
return level;
}
}

export function defSortedMap<K, V>(
pairs?: Iterable<Pair<K, V>> | null,
opts?: Partial<SortedMapOpts<K>>
): SortedMap<K, V>;
export function defSortedMap<V>(
obj: IObjectOf<V>,
opts?: Partial<SortedMapOpts<string>>
): SortedMap<string, V>;
export function defSortedMap<V>(
src: any,
opts?: Partial<SortedMapOpts<any>>
): SortedMap<any, V> {
if (isPlainObject(src)) {
const keys = Object.keys(src);
return new SortedMap<string, V>(
map((k) => <Pair<string, V>>[k, (<IObjectOf<V>>src)[k]], keys),
{
capacity: keys.length,
...opts,
}
);
} else {
return new SortedMap(src, opts);
}
}
5 changes: 5 additions & 0 deletions packages/associative/src/sorted-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,8 @@ export class SortedSet<T> extends Set<T>
return __private.get(this)!.opts();
}
}

export const defSortedSet = <T>(
vals?: Iterable<T> | null,
opts?: Partial<SortedSetOpts<T>>
) => new SortedSet(vals, opts);
2 changes: 1 addition & 1 deletion packages/associative/src/sparse-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export class SparseSet32 extends ASparseSet<Uint32Array>
*
* @param n - max capacity, ID range: [0...n)
*/
export const sparseSet = (n: number) =>
export const defSparseSet = (n: number) =>
n <= 0x100
? new SparseSet8(n)
: n <= 0x10000
Expand Down
52 changes: 43 additions & 9 deletions packages/associative/test/sorted-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ 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";
import { defSortedMap, SortedMap } from "../src/sorted-map";

describe("SortedMap", () => {
let m: SortedMap<any, any>;

beforeEach(() => {
m = SortedMap.fromObject({ a: 1, b: 2, c: 3 });
m = defSortedMap({ a: 1, b: 2, c: 3 });
});

it("size", () => {
Expand Down Expand Up @@ -70,23 +70,43 @@ describe("SortedMap", () => {
});

it("entries", () => {
assert.deepEqual([...m], [["a", 1], ["b", 2], ["c", 3]]);
assert.deepEqual(
[...m],
[
["a", 1],
["b", 2],
["c", 3],
]
);
});

// it("entries rev", () => {
// assert.deepEqual([...m.entries(undefined, true)], [["c", 3], ["b", 2], ["a", 1]]);
// });

it("entries a", () => {
assert.deepEqual([...m.entries("a")], [["a", 1], ["b", 2], ["c", 3]]);
assert.deepEqual(
[...m.entries("a")],
[
["a", 1],
["b", 2],
["c", 3],
]
);
});

// it("entries a rev", () => {
// assert.deepEqual([...m.entries("a", true)], [["a", 1]]);
// });

it("entries aa", () => {
assert.deepEqual([...m.entries("aa")], [["b", 2], ["c", 3]]);
assert.deepEqual(
[...m.entries("aa")],
[
["b", 2],
["c", 3],
]
);
});

// it("entries aa rev", () => {
Expand All @@ -110,7 +130,14 @@ describe("SortedMap", () => {
// });

it("entries 0", () => {
assert.deepEqual([...m.entries("0")], [["a", 1], ["b", 2], ["c", 3]]);
assert.deepEqual(
[...m.entries("0")],
[
["a", 1],
["b", 2],
["c", 3],
]
);
});

// it("entries 0 rev", () => {
Expand Down Expand Up @@ -140,14 +167,21 @@ describe("SortedMap", () => {
});

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

it("fuzz", () => {
Expand Down
Loading

0 comments on commit 48ae24a

Please sign in to comment.