Skip to content

Commit

Permalink
refactor(associative): add private impls for EquivMap/Set
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Apr 12, 2018
1 parent cb4976f commit a769856
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 60 deletions.
3 changes: 3 additions & 0 deletions packages/associative/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
"join",
"map",
"set",
"skiplist",
"sorted map",
"sorted set",
"typescript",
"union"
],
Expand Down
79 changes: 48 additions & 31 deletions packages/associative/src/equiv-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ import { equiv } from "@thi.ng/api/equiv";
import { SEMAPHORE, Pair } from "./api";
import { EquivSet } from "./equiv-set";

interface EqMapProps<K, V> {
keys: EquivSet<K>;
map: Map<K, V>;
}

export interface EqMapOpts<K> {
equiv: Predicate2<K>;
}

const __private = new WeakMap<EquivMap<any, any>, EqMapProps<any, any>>();

export class EquivMap<K, V> extends Map<K, V> implements
Iterable<Pair<K, V>>,
ICopy<EquivMap<K, V>>,
Expand All @@ -20,16 +31,11 @@ export class EquivMap<K, V> extends Map<K, V> implements
return m;
}

protected _keys: EquivSet<K>;
protected _map: Map<K, V>;

constructor(pairs?: Iterable<Pair<K, V>>, eq: Predicate2<K> = equiv) {
super();
this._keys = new EquivSet<K>(null, eq);
this._map = new Map<K, V>();
Object.defineProperties(this, {
_keys: { enumerable: false },
_map: { enumerable: false }
__private.set(this, {
keys: new EquivSet<K>(null, eq),
map: new Map<K, V>(),
});
if (pairs) {
this.into(pairs);
Expand All @@ -45,22 +51,26 @@ export class EquivMap<K, V> extends Map<K, V> implements
}

get size() {
return this._keys.size;
return __private.get(this).keys.size;
}

clear() {
this._keys.clear();
this._map.clear();
const $this = __private.get(this);
$this.keys.clear();
$this.map.clear();
}

empty() {
return new EquivMap<K, V>(null, (<any>this._keys)._equiv);
return new EquivMap<K, V>(null, __private.get(this).keys.getOpts().equiv);
}

copy() {
const m = new EquivMap<K, V>(null, (<any>this._keys)._equiv);
m._keys = this._keys.copy();
m._map = new Map<K, V>(this._map);
const $this = __private.get(this);
const m = new EquivMap<K, V>();
__private.set(m, {
keys: $this.keys.copy(),
map: new Map<K, V>($this.map)
});
return m;
}

Expand All @@ -74,19 +84,20 @@ export class EquivMap<K, V> extends Map<K, V> implements
if (this.size !== o.size) {
return false;
}
for (let k of this._map.keys()) {
if (!equiv(o.get(k), this._map.get(k))) {
for (let k of __private.get(this).map.keys()) {
if (!equiv(o.get(k), __private.get(this).map.get(k))) {
return false;
}
}
return true;
}

delete(key: K) {
key = this._keys.get(key, SEMAPHORE);
const $this = __private.get(this);
key = $this.keys.get(key, SEMAPHORE);
if (key !== <any>SEMAPHORE) {
this._map.delete(key);
this._keys.delete(key);
$this.map.delete(key);
$this.keys.delete(key);
return true;
}
return false;
Expand All @@ -100,30 +111,32 @@ export class EquivMap<K, V> extends Map<K, V> implements
}

forEach(fn: (val: V, key: K, map: Map<K, V>) => void, thisArg?: any) {
for (let pair of this._map) {
for (let pair of __private.get(this).map) {
fn.call(thisArg, pair[1], pair[0], this);
}
}

get(key: K, notFound?: any) {
key = this._keys.get(key, SEMAPHORE);
const $this = __private.get(this);
key = $this.keys.get(key, SEMAPHORE);
if (key !== <any>SEMAPHORE) {
return this._map.get(key);
return $this.map.get(key);
}
return notFound;
}

has(key: K) {
return this._keys.has(key);
return __private.get(this).keys.has(key);
}

set(key: K, value: V) {
const k = this._keys.get(key, SEMAPHORE);
const $this = __private.get(this);
const k = $this.keys.get(key, SEMAPHORE);
if (k !== <any>SEMAPHORE) {
this._map.set(k, value);
$this.map.set(k, value);
} else {
this._keys.add(key);
this._map.set(key, value);
$this.keys.add(key);
$this.map.set(key, value);
}
return this;
}
Expand All @@ -136,14 +149,18 @@ export class EquivMap<K, V> extends Map<K, V> implements
}

entries() {
return this._map.entries();
return __private.get(this).map.entries();
}

keys() {
return this._map.keys();
return __private.get(this).map.keys();
}

values() {
return this._map.values();
return __private.get(this).map.values();
}

getOpts(): EqMapOpts<K> {
return __private.get(this).keys.getOpts();
}
}
68 changes: 39 additions & 29 deletions packages/associative/src/equiv-set.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { ICopy, IEmpty, IEquiv, Predicate2 } from "@thi.ng/api/api";
import { equiv } from "@thi.ng/api/equiv";
import { DCons } from "@thi.ng/dcons";
import { SEMAPHORE } from "./api";
import { SEMAPHORE, Pair } from "./api";

interface EqSetProps<T> {
vals: DCons<T>;
equiv: Predicate2<T>;
}

export interface EqSetOpts<T> {
equiv: Predicate2<T>;
}

const __private = new WeakMap<EquivSet<any>, EqSetProps<any>>();

/**
* An alternative set implementation to the native ES6 Set type. Uses
Expand All @@ -18,54 +29,47 @@ export class EquivSet<T> extends Set<T> implements
IEmpty<EquivSet<T>>,
IEquiv {

protected _vals: DCons<T>;
protected _equiv: Predicate2<T>;

constructor(vals?: Iterable<T>, eq: Predicate2<T> = equiv) {
super();
this._equiv = eq;
this._vals = new DCons<T>();
Object.defineProperties(this, {
_vals: { enumerable: false },
_equiv: { enumerable: false }
});
__private.set(this, { equiv: eq, vals: new DCons<T>() });
vals && this.into(vals);
}

*[Symbol.iterator]() {
yield* this._vals;
yield* __private.get(this).vals;
}

get [Symbol.species]() {
return EquivSet;
}

get size() {
return this._vals.length;
return __private.get(this).vals.length;
}

copy() {
const s = new EquivSet<T>(null, this._equiv);
s._vals = this._vals.copy();
const $this = __private.get(this);
const s = new EquivSet<T>(null, $this.equiv);
__private.get(s).vals = $this.vals.copy();
return s;
}

empty() {
return new EquivSet<T>(null, this._equiv);
return new EquivSet<T>(null, __private.get(this).equiv);
}

clear() {
this._vals.clear();
__private.get(this).vals.clear();
}

first() {
if (this.size) {
return this._vals.head.value;
return __private.get(this).vals.head.value;
}
}

add(x: T) {
!this.has(x) && this._vals.push(x);
!this.has(x) && __private.get(this).vals.push(x);
return this;
}

Expand All @@ -88,8 +92,9 @@ export class EquivSet<T> extends Set<T> implements
* @param notFound
*/
get(x: T, notFound?: any) {
const eq = this._equiv;
let i = this._vals.head;
const $this = __private.get(this);
const eq = $this.equiv;
let i = $this.vals.head;
while (i) {
if (eq(i.value, x)) {
return i.value;
Expand All @@ -100,11 +105,12 @@ export class EquivSet<T> extends Set<T> implements
}

delete(x: T) {
const eq = this._equiv;
let i = this._vals.head;
const $this = __private.get(this)
const eq = $this.equiv;
let i = $this.vals.head;
while (i) {
if (eq(i.value, x)) {
this._vals.splice(i, 1);
$this.vals.splice(i, 1);
return true;
}
i = i.next;
Expand All @@ -129,7 +135,7 @@ export class EquivSet<T> extends Set<T> implements
if (this.size !== o.size) {
return false;
}
let i = this._vals.head;
let i = __private.get(this).vals.head;
while (i) {
if (!o.has(i.value)) {
return false;
Expand All @@ -140,24 +146,28 @@ export class EquivSet<T> extends Set<T> implements
}

forEach(fn: (val: T, val2: T, set: Set<T>) => void, thisArg?: any) {
let i = this._vals.head;
let i = __private.get(this).vals.head;
while (i) {
fn.call(thisArg, i.value, i.value, this);
i = i.next;
}
}

*entries(): IterableIterator<[T, T]> {
for (let v of this._vals) {
*entries(): IterableIterator<Pair<T, T>> {
for (let v of __private.get(this).vals) {
yield [v, v];
}
}

*keys() {
yield* this._vals;
yield* __private.get(this).vals;
}

*values() {
yield* this._vals;
yield* this.keys();
}

getOpts(): EqSetOpts<T> {
return { equiv: __private.get(this).equiv };
}
}

0 comments on commit a769856

Please sign in to comment.