Skip to content

Commit

Permalink
feat: complex KeyLabel allowed as elemnets + extra types, utils and t…
Browse files Browse the repository at this point in the history
…ests
  • Loading branch information
eturino committed Feb 28, 2020
1 parent a9cc9ff commit a3fdec3
Show file tree
Hide file tree
Showing 27 changed files with 389 additions and 98 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ We have 4 classes:
- `KeySetSome`: represents a concrete set (`A ⊂ 𝕌`)
- `KeySetAllExceptSome`: represents the complementary of a set, all the elements except the given ones (`A' = {x ∈ 𝕌 | x ∉ A}`) _(see [Complement in Wikipedia](https://en.wikipedia.org/wiki/Complement_\(set*theory\)))*

We can have a KeySet of:

- `string`s
- `number`s
- objects with `key` (`string` or `number`) and `label` (`string`)

All elements have to have be of the same type.

### Creation: `all()`, `none()`, `some([...])`, `allExceptSome([...])`, `someForced([...])`, `allExceptSomeForced([...])`

Build your KeySets using the build functions
Expand Down Expand Up @@ -58,6 +66,17 @@ allExceptSomeForced([1, 3, 2, 3]); // returns a new instance of KeySetAllExceptS
allExceptSomeForced([]); // throws an InvalidEmptySetError
```

### `.elements` (aliased with `.keys`)

Both getters return a copy of the internal list of elements.

```
some([1, 3, 2, 3]).elements; // => [1, 2, 3]
some([1, 3, 2, 3]).keys; // => [1, 2, 3]
allExceptSome([1, 3, 2, 3]).elements; // => [1, 2, 3]
allExceptSome([1, 3, 2, 3]).keys; // => [1, 2, 3]
```

### `type`

All KeySet expose a `type` property that will return a member of the `KeySetTypes` enum.
Expand Down
73 changes: 72 additions & 1 deletion src/lib/__tests__/key-set.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
all,
allExceptSome,
allExceptSomeForced,
isValidKey,
IKeyLabel,
InvalidEmptySetError,
KeySetAll,
KeySetAllExceptSome,
Expand All @@ -12,7 +14,28 @@ import {
none,
some,
someForced
} from "../key-set";
} from "../..";

describe("isValidKey()", () => {
it("isValidKey(1): true", () => {
expect(isValidKey(1)).toBeTruthy();
});
it("isValidKey('1'): true", () => {
expect(isValidKey("1")).toBeTruthy();
});
it("isValidKey({}): false", () => {
expect(isValidKey({})).toBeFalsy();
});
it("isValidKey({key: 1}): false", () => {
expect(isValidKey({ key: 1 })).toBeFalsy();
});
it("isValidKey({key: 1, label: 'A'}): true", () => {
expect(isValidKey({ key: 1, label: "A" })).toBeTruthy();
});
it("isValidKey({key: 1, label: 'A', other: 'stuff'}): true", () => {
expect(isValidKey({ key: 1, label: "A", other: "stuff" })).toBeTruthy();
});
});

test("all()", () => {
const keySet = all();
Expand Down Expand Up @@ -54,27 +77,75 @@ test("some([1, 2, 3])", () => {
const keySet = some([1, 2, 3]);
expect(keySet instanceof KeySetSome).toBeTruthy();
expect((keySet as KeySetSome<number>).keys).toEqual([1, 2, 3]);
expect((keySet as KeySetSome<number>).elements).toEqual([1, 2, 3]);
expect(keySet.type).toEqual(KeySetTypes.some);
});

test("allExceptSome([1, 2, 3])", () => {
const keySet = allExceptSome([1, 2, 3]);
expect(keySet instanceof KeySetAllExceptSome).toBeTruthy();
expect((keySet as KeySetAllExceptSome<number>).keys).toEqual([1, 2, 3]);
expect((keySet as KeySetAllExceptSome<number>).elements).toEqual([1, 2, 3]);
expect(keySet.type).toEqual(KeySetTypes.allExceptSome);
});

test("allExceptSome([{ key: 1, label: 'wa' }, { key: 1, label: 'B' }, { key: 1, label: 'wa' }, { key: 2, label: 'wa' }, { key: 3, label: 'other' }])", () => {
const keySet = allExceptSome([
{ key: 1, label: "wa" },
{ key: 1, label: "B" },
{ key: 1, label: "wa" },
{ key: 2, label: "wa" },
{ key: 3, label: "other" }
]);
expect(keySet instanceof KeySetAllExceptSome).toBeTruthy();
expect((keySet as KeySetAllExceptSome<IKeyLabel<number>>).keys).toEqual([
{ key: 1, label: "wa" },
{ key: 2, label: "wa" },
{ key: 3, label: "other" }
]);
expect((keySet as KeySetAllExceptSome<IKeyLabel<number>>).elements).toEqual([
{ key: 1, label: "wa" },
{ key: 2, label: "wa" },
{ key: 3, label: "other" }
]);
expect(keySet.type).toEqual(KeySetTypes.allExceptSome);
});

test("some([{ key: 1, label: 'wa' }, { key: 1, label: 'B' }, { key: 1, label: 'wa' }, { key: 2, label: 'wa' }, { key: 3, label: 'other' }])", () => {
const keySet = some([
{ key: 1, label: "wa" },
{ key: 1, label: "B" },
{ key: 1, label: "wa" },
{ key: 2, label: "wa" },
{ key: 3, label: "other" }
]);
expect(keySet instanceof KeySetSome).toBeTruthy();
expect((keySet as KeySetSome<IKeyLabel<number>>).keys).toEqual([
{ key: 1, label: "wa" },
{ key: 2, label: "wa" },
{ key: 3, label: "other" }
]);
expect((keySet as KeySetSome<IKeyLabel<number>>).elements).toEqual([
{ key: 1, label: "wa" },
{ key: 2, label: "wa" },
{ key: 3, label: "other" }
]);
expect(keySet.type).toEqual(KeySetTypes.some);
});

test("someForced([1, 2, 3])", () => {
const keySet = someForced([1, 2, 3]);
expect(keySet instanceof KeySetSome).toBeTruthy();
expect((keySet as KeySetSome<number>).keys).toEqual([1, 2, 3]);
expect((keySet as KeySetSome<number>).elements).toEqual([1, 2, 3]);
expect(keySet.type).toEqual(KeySetTypes.some);
});

test("allExceptSomeForced([1, 2, 3])", () => {
const keySet = allExceptSomeForced([1, 2, 3]);
expect(keySet instanceof KeySetAllExceptSome).toBeTruthy();
expect((keySet as KeySetAllExceptSome<number>).keys).toEqual([1, 2, 3]);
expect((keySet as KeySetAllExceptSome<number>).elements).toEqual([1, 2, 3]);
expect(keySet.type).toEqual(KeySetTypes.allExceptSome);
});

Expand Down
44 changes: 44 additions & 0 deletions src/lib/__tests__/serialize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,28 @@ describe("serialize KeySet", () => {
});
});

describe("some([{ key: 'a', label: 'A' }, { key: 'b', label: 'B' }])", () => {
const keySet = some([
{ key: "a", label: "A" },
{ key: "b", label: "B" }
]);
const expected = {
type: KeySetTypes.some,
elements: [
{ key: "a", label: "A" },
{ key: "b", label: "B" }
]
};

it("keySet.serialized()", () => {
expect(keySet.serialized()).toEqual(expected);
});

it("serializeKeySet(keySet)", () => {
expect(serializeKeySet(keySet)).toEqual(expected);
});
});

describe("allExceptSome(['1', '2'])", () => {
const keySet = allExceptSome(["1", "2"]);
const expected = {
Expand Down Expand Up @@ -376,4 +398,26 @@ describe("serialize KeySet", () => {
expect(serializeKeySet(keySet)).toEqual(expected);
});
});

describe("allExceptSome([{ key: 'a', label: 'A' }, { key: 'b', label: 'B' }])", () => {
const keySet = allExceptSome([
{ key: "a", label: "A" },
{ key: "b", label: "B" }
]);
const expected = {
type: KeySetTypes.allExceptSome,
elements: [
{ key: "a", label: "A" },
{ key: "b", label: "B" }
]
};

it("keySet.serialized()", () => {
expect(keySet.serialized()).toEqual(expected);
});

it("serializeKeySet(keySet)", () => {
expect(serializeKeySet(keySet)).toEqual(expected);
});
});
});
12 changes: 10 additions & 2 deletions src/lib/key-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
isKeySetAllExceptSome,
isKeySetNone,
isKeySetSome,
isValidKey,
Key,
KeySet,
KeySetAllExceptSomeSerialized,
Expand Down Expand Up @@ -39,7 +40,8 @@ import {
NonEmptyArray
} from "./util/array-types";
import { arraysEqual } from "./util/arrays-equal";
import { uniqueArray } from "./util/unique-array";
import { IKeyLabel, isKeyLabel, isObject } from "./util/object-utils";
import { uniqueArray, uniqueKeyLabelArray } from "./util/unique-array";

export {
// builders
Expand All @@ -61,6 +63,7 @@ export {
KeySetAllSerialized,
KeySetNoneSerialized,
KeySetSomeSerialized,
IKeyLabel,
InvalidKeySetError,
InvalidEmptySetError,
// enums
Expand All @@ -70,6 +73,7 @@ export {
parseKeySet,
// util functions
uniqueArray,
uniqueKeyLabelArray,
arraysEqual,
// util types
EmptyArray,
Expand All @@ -86,5 +90,9 @@ export {
isKeySetAllSerialized,
isKeySetNoneSerialized,
isKeySetSomeSerialized,
isKeySetAllExceptSomeSerialized
isKeySetAllExceptSomeSerialized,
// utils
isValidKey,
isKeyLabel,
isObject
};
8 changes: 7 additions & 1 deletion src/lib/key-set/-base.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
/* tslint:disable:interface-over-type-literal */
import { IKeyLabel, isKeyLabel } from "../key-set";
import { EmptyArray, NonEmptyArray } from "../util/array-types";
import { KeySetAll } from "./all";
import { KeySetAllExceptSome } from "./all-except-some";
import { KeySetNone } from "./none";
import { KeySetSome } from "./some";

export type Key = string | number;
export type Key = string | number | IKeyLabel<string> | IKeyLabel<number>;

export function isValidKey(x: any): x is Key {
if (typeof x === "string" || typeof x === "number") return true;
return isKeyLabel(x);
}

export type KeySet<T extends Key = Key> =
| KeySetAll
Expand Down
16 changes: 10 additions & 6 deletions src/lib/key-set/-by-keys.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NonEmptyArray } from "../util/array-types";
import { arraysEqual } from "../util/arrays-equal";
import { uniqueArray } from "../util/unique-array";
import { uniqueKeys } from "../util/unique-array";
import {
IKeySetClass,
Key,
Expand All @@ -16,14 +16,14 @@ import { KeySetSome } from "./some";
export abstract class KeySetByKeys<T extends Key> implements IKeySetClass {
public abstract readonly type: KeySetTypes.allExceptSome | KeySetTypes.some;

private readonly keySet: NonEmptyArray<T>;
private readonly _elements: NonEmptyArray<T>;

constructor(keys: T[] | ReadonlyArray<T>) {
const keySet = uniqueArray(keys).sort();
if (keySet.length === 0) {
const elements = uniqueKeys(keys).sort();
if (elements.length === 0) {
throw new InvalidEmptySetError();
}
this.keySet = keySet as NonEmptyArray<T>;
this._elements = elements as NonEmptyArray<T>;
}

public abstract serialized():
Expand All @@ -49,7 +49,11 @@ export abstract class KeySetByKeys<T extends Key> implements IKeySetClass {
public abstract intersect(other: KeySet): KeySet;

public get keys(): T[] {
return [...this.keySet];
return this.elements;
}

public get elements(): T[] {
return [...this._elements];
}

protected hasSameKeys(other: KeySetByKeys<Key>): boolean {
Expand Down
3 changes: 1 addition & 2 deletions src/lib/key-set/__tests__/all-except-some.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { KeySetAllExceptSome } from "../all-except-some";
import { KeySetSome } from "../some";
import { KeySetAllExceptSome, KeySetSome } from "../../..";

const keySet = new KeySetAllExceptSome([3, 1, 2, 3, 2, 1]); // => keys 1, 2, 3

Expand Down
3 changes: 1 addition & 2 deletions src/lib/key-set/__tests__/all.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { KeySetAll } from "../all";
import { KeySetNone } from "../none";
import { KeySetAll, KeySetNone } from "../../..";

const keySet = new KeySetAll();

Expand Down
10 changes: 6 additions & 4 deletions src/lib/key-set/__tests__/intersect/all-except-some.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { KeySetAll } from "../../all";
import { KeySetAllExceptSome } from "../../all-except-some";
import { KeySetNone } from "../../none";
import { KeySetSome } from "../../some";
import {
KeySetAll,
KeySetAllExceptSome,
KeySetNone,
KeySetSome
} from "../../../..";

const keySetAll = new KeySetAll();
const keySetNone = new KeySetNone();
Expand Down
12 changes: 7 additions & 5 deletions src/lib/key-set/__tests__/intersect/all.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { KeySetAll } from "../../all";
import { KeySetAllExceptSome } from "../../all-except-some";
import { InvalidKeySetError } from "../../invalid-key-set-error";
import { KeySetNone } from "../../none";
import { KeySetSome } from "../../some";
import {
InvalidKeySetError,
KeySetAll,
KeySetAllExceptSome,
KeySetNone,
KeySetSome
} from "../../../..";

const keySetAll = new KeySetAll();
const keySetNone = new KeySetNone();
Expand Down
10 changes: 6 additions & 4 deletions src/lib/key-set/__tests__/intersect/none.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { KeySetAll } from "../../all";
import { KeySetAllExceptSome } from "../../all-except-some";
import { KeySetNone } from "../../none";
import { KeySetSome } from "../../some";
import {
KeySetAll,
KeySetAllExceptSome,
KeySetNone,
KeySetSome
} from "../../../..";

const keySetAll = new KeySetAll();
const keySetNone = new KeySetNone();
Expand Down
10 changes: 6 additions & 4 deletions src/lib/key-set/__tests__/intersect/some.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { KeySetAll } from "../../all";
import { KeySetAllExceptSome } from "../../all-except-some";
import { KeySetNone } from "../../none";
import { KeySetSome } from "../../some";
import {
KeySetAll,
KeySetAllExceptSome,
KeySetNone,
KeySetSome
} from "../../../..";

const keySetAll = new KeySetAll();
const keySetNone = new KeySetNone();
Expand Down
10 changes: 6 additions & 4 deletions src/lib/key-set/__tests__/is-equal/all-except-some.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { KeySetAll } from "../../all";
import { KeySetAllExceptSome } from "../../all-except-some";
import { KeySetNone } from "../../none";
import { KeySetSome } from "../../some";
import {
KeySetAll,
KeySetAllExceptSome,
KeySetNone,
KeySetSome
} from "../../../..";

const keySetAll = new KeySetAll();
const keySetNone = new KeySetNone();
Expand Down
Loading

0 comments on commit a3fdec3

Please sign in to comment.