Skip to content

Commit

Permalink
Merge pull request #11 from faramozzayw/set-rust
Browse files Browse the repository at this point in the history
HashSet
  • Loading branch information
faramozzayw authored Aug 25, 2021
2 parents 252c8fd + c38dbb2 commit 370bd82
Show file tree
Hide file tree
Showing 5 changed files with 396 additions and 1 deletion.
151 changes: 151 additions & 0 deletions __test__/hash-set.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { HashSet } from "../src";

describe("Option", () => {
it("`isDisjoint` works correctly", () => {
expect(
new HashSet([2, 5, 1, 3]).isDisjoint(new HashSet([20])),
).toBeTruthy();

expect(new HashSet([2, 5, 1, 3]).isDisjoint(new HashSet([2]))).toBeFalsy();
});

describe("isSuperset", () => {
it("works correct when set a empty", () => {
const isSuperset = new HashSet([]).isSuperset(new HashSet([]));

expect(isSuperset).toBeTruthy();
});

it("works correct with empty set", () => {
const isSuperset = new HashSet([1, 2, 3, 4]).isSuperset(new HashSet([]));

expect(isSuperset).toBeTruthy();
});

it("works correct when is superset", () => {
const isSuperset = new HashSet([1, 2, 3, 4]).isSuperset(
new HashSet([2, 3]),
);

expect(isSuperset).toBeTruthy();
});

it("works correct when is NOT superset", () => {
const isSuperset = new HashSet([1, 2, 3, 4]).isSuperset(
new HashSet([15]),
);

expect(isSuperset).toBeFalsy();
});
});

describe("union", () => {
it("works correctly without same values", () => {
const diff = new HashSet([2, 5, 1, 3]).union(new HashSet([20]));
expect(diff.toArray()).toEqual([2, 5, 1, 3, 20]);
});

it("works correctly with same values", () => {
const diff = new HashSet([2, 5, 1, 3]).union(new HashSet([2, 20]));
expect(diff.toArray()).toEqual([2, 5, 1, 3, 20]);
});
});

describe("intersection", () => {
it("works correctly without same values", () => {
const intersection = new HashSet([1, 2, 3]).intersection(
new HashSet([4, 5]),
);
expect(intersection.toArray()).toEqual([]);
});

it("works correctly with same values", () => {
const intersection = new HashSet([1, 2, 3]).intersection(
new HashSet([4, 2, 3, 4]),
);
expect(intersection.toArray()).toEqual([2, 3]);
});
});

describe("`difference` method", () => {
it("works correctly with no diff", () => {
const diff = new HashSet([2, 5, 1, 3]).difference(new HashSet([20]));
expect(diff.toArray()).toEqual([2, 5, 1, 3]);
});

it("works correctly with no diff", () => {
const diff = new HashSet([2, 5, 1, 3]).difference(new HashSet([2]));
expect(diff.toArray()).toEqual([5, 1, 3]);
});
});

describe("symmetricDifference", () => {
it("works correctly with different values", () => {
const diff = new HashSet([1, 2, 3, 5]).symmetricDifference(
new HashSet([1, 2, 3, 4]),
);

expect(diff.toArray()).toEqual([5, 4]);
});

it("works correctly without different values", () => {
const diff = new HashSet([1, 2, 3]).symmetricDifference(
new HashSet([1, 2, 3]),
);

expect(diff.toArray()).toEqual([]);
});
});

describe("drain", () => {
it("works correctly with empty set", () => {
const emptySet = new HashSet();

expect(emptySet.drain()).toEqual([]);
expect(emptySet.isEmpty()).toBeTruthy();
});

it("works correctly with a set that has values", () => {
const set = new HashSet([1, 2, 3, 4]);

expect(set.drain()).toEqual([1, 2, 3, 4]);
expect(set.isEmpty()).toBeTruthy();
});
});

describe("drainFilter", () => {
it("works correctly with empty set", () => {
const emptySet = new HashSet();

expect(emptySet.drainFilter((v) => v !== null)).toEqual([]);
expect(emptySet.isEmpty()).toBeTruthy();
});

it("works correctly with a set that has values", () => {
const set = new HashSet([1, 2, 3, 4]);

expect(set.drainFilter((v) => v % 2 === 0)).toEqual([2, 4]);
expect(set.isEmpty()).toBeTruthy();
});
});

describe("retain", () => {
it("works correctly with empty set", () => {
const emptySet = new HashSet();

emptySet.retain((v) => v !== null);

expect(emptySet.toArray()).toEqual([]);
expect(emptySet.isEmpty()).toBeTruthy();
});

it("works correctly with a set that has values", () => {
const set = new HashSet([1, 2, 3, 4]);

set.retain((v) => v % 2 === 0);

expect(set.toArray()).toEqual([2, 4]);
expect(set.isEmpty()).toBeFalsy();
});
});
});
236 changes: 236 additions & 0 deletions src/HashSet/hash-set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import { None, Option, Some } from "../Option";
import { Clone } from "../utils";
import { Vector } from "../Vector";

/**
* @class HashSet
*
* @template T
*
* ### Definition ([source](https://en.wikipedia.org/wiki/Set_(abstract_data_type))):
* A **set** is an abstract data type that can store unique values, without any particular order. It is a computer implementation of the mathematical concept of a finite set. Unlike most other collection types, rather than retrieving a specific element from a set, one typically tests a value for membership in a set.
*
*
* #### Use the Set variant of any of these Maps when: ([source](https://doc.rust-lang.org/std/collections/index.html#use-the-set-variant-of-any-of-these-maps-when))
* - You just want to remember which keys you’ve seen.
* - There is no meaningful value to associate with your keys.
* - You just want a set.
*
* #### References
* @see [JS Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)
* @see [Rust HashSet](https://doc.rust-lang.org/std/collections/struct.HashSet.html)
* @see [The Empty Set](https://www.math.drexel.edu/~tolya/emptyset.pdf)
*
* @category HashSet
*/
export class HashSet<T> extends Set<T> implements Clone<HashSet<T>> {
public static get [Symbol.species]() {
return Set;
}

public constructor(values?: Iterable<T>) {
super(values);
}

public clone(): HashSet<T> {
return new HashSet(this);
}

public toArray(): T[] {
return [...this];
}

public toVec(): Vector<T> {
return Vector.fromArray(this.toArray());
}

/**
* Returns `true` if the set is a superset of another, i.e., `this` contains at least all the values in `other`.
*
* ### Example
* ```ts
* const isSuperset = new HashSet([1, 2, 3, 4]).isSuperset(new HashSet([15]));
* expect(isSuperset).toBeFalsy();
*
* const isSuperset = new HashSet([1, 2, 3, 4]).isSuperset(new HashSet([2, 3]));
* expect(isSuperset).toBeTruthy();
*
* const isSuperset = new HashSet([1, 2, 3, 4]).isSuperset(new HashSet([]));
* expect(isSuperset).toBeTruthy();
* ```
*
* ### Q/A
* - Is the empty set a superset of itself?
* **Yes. A ⊆ B ⟺ B ⊇ A so .... ∅ ⊆ ∅ ⟺ ∅ ⊇ ∅**
* @see [More about it](https://math.stackexchange.com/questions/334666/is-the-empty-set-a-subset-of-itself)
*/
public isSuperset(other: HashSet<T>): boolean {
return other.isSubset(this);
}

/**
* Returns `true` if the set is a subset of another, i.e., `other` contains at least all the values in `this`.
*/
public isSubset(other: HashSet<T>): boolean {
if (this.size <= other.size) {
return this.toArray().every((value) => other.has(value));
}

return false;
}

/**
* Returns `true` if `this` has no elements in common with `other`. This is equivalent to checking for an empty intersection.
*
* ### Example
*
* ```ts
* expect(new HashSet([2, 5, 1, 3]).isDisjoint(new HashSet([20]))).toBeTruthy();
* expect(new HashSet([2, 5, 1, 3]).isDisjoint(new HashSet([2]))).toBeFalsy();
* ```
*/
public isDisjoint(other: HashSet<T>): boolean {
return !this.toArray().some((value) => other.has(value));
}

/**
* Visits the values representing the difference, i.e., the values that are in `this` but not in `other`.
*
* ### Example
*
* ```ts
* const diff = new HashSet([2, 5, 1, 3]).difference(new HashSet([2]));
* expect(diff.toArray()).toEqual([5, 1, 3]);
*
* const diff = new HashSet([2, 5, 1, 3]).difference(new HashSet([20]));
* expect(diff.toArray()).toEqual([2, 5, 1, 3]);
* ```
*/
public difference(other: HashSet<T>): HashSet<T> {
let diff = this.clone();

other.forEach((value) => diff.delete(value));

return diff;
}

/**
* Visits the values representing the symmetric difference, i.e., the values that are in `this` or in `other` but not in both.
*
* ### Example
*
* ```ts
* const diff = new HashSet([1, 2, 3]).symmetricDifference(new HashSet([1, 2, 3]));
* expect(diff.toArray()).toEqual([]);
*
* const diff = new HashSet([1, 2, 3, 5]).symmetricDifference(new HashSet([1, 2, 3, 4]));
* expect(diff.toArray()).toEqual([5, 4]);
* ```
*/
public symmetricDifference(other: HashSet<T>): HashSet<T> {
const diffSelf = this.difference(other);
const diffOther = other.difference(this);

return diffSelf.union(diffOther);
}

/**
* Visits the values representing the intersection, i.e., the values that are both in `this` and `other`.
*
* ### Example
*
* ```ts
* const intersection = new HashSet([1, 2, 3]).intersection(new HashSet([4, 5]));
* expect(intersection.toArray()).toEqual([]);
*
* const intersection = new HashSet([1, 2, 3]).intersection(new HashSet([4, 2, 3, 4]));
* expect(intersection.toArray()).toEqual([2, 3]);
* ```
*/
public intersection(other: HashSet<T>): HashSet<T> {
let inters = new HashSet<T>();

other.forEach((value) => this.has(value) && inters.add(value));

return inters;
}

/**
* Visits the values representing the union, i.e., all the values in `this` or `other`, without duplicates.
*
* ### Example
*
* ```ts
* const diff = new HashSet([2, 5, 1, 3]).union(new HashSet([20]));
* expect(diff.toArray()).toEqual([2, 5, 1, 3, 20]);
*
* const diff = new HashSet([2, 5, 1, 3]).union(new HashSet([2, 20]));
* expect(diff.toArray()).toEqual([2, 5, 1, 3, 20]);
* ```
*/
public union(other: HashSet<T>): HashSet<T> {
return new HashSet<T>(this.toArray().concat(other.toArray()));
}

/**
* Clears the set, returning all elements in an `Iterable`.
*
* ### Example
* ```ts
* const set = new HashSet([1, 2, 3, 4]);
*
* expect(set.drain()).toEqual([1, 2, 3, 4]);
* expect(set.isEmpty()).toBeTruthy();
* ```
*/
public drain(): Array<T> {
const elements = [...this];

this.clear();

return elements;
}

/**
*
* ### Example
* ```ts
* const set = new HashSet([1, 2, 3, 4]);
*
* expect(set.drainFilter((v) => v % 2 === 0)).toEqual([2, 4]);
* expect(set.isEmpty()).toBeTruthy();
* ```
*/
public drainFilter<F extends (value: T, index: number) => boolean>(
fn: F,
): Array<T> {
return this.drain().filter(fn);
}

/**
* Retains only the elements specified by the predicate.
*
* In other words, remove all elements `e` such that `fn(e)` returns `false`.
*
* ### Example
* ```ts
* const set = new HashSet([1, 2, 3, 4]);
*
* set.retain((v) => v % 2 === 0);
*
* expect(set.toArray()).toEqual([2, 4]);
* expect(set.isEmpty()).toBeFalsy();
* ```
*/
public retain<F extends (value: T) => boolean>(fn: F): void {
for (let element of this) {
if (!fn(element)) {
this.delete(element);
}
}
}

public isEmpty(): boolean {
return this.size === 0;
}
}
1 change: 1 addition & 0 deletions src/HashSet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./hash-set";
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./HashSet";
export { Vector } from "./Vector";
export { Err, Ok, Result } from "./Result";
export { Some, None, Option } from "./Option";
Expand Down
Loading

1 comment on commit 370bd82

@vercel
Copy link

@vercel vercel bot commented on 370bd82 Aug 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.