Skip to content

Commit

Permalink
added properSubset & properSuperset functions (#36)
Browse files Browse the repository at this point in the history
* added `properSubset` & `properSuperset` functions

* fixed linting
  • Loading branch information
kubikowski authored Oct 20, 2022
1 parent e857ced commit f984da5
Show file tree
Hide file tree
Showing 16 changed files with 365 additions and 25 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ const isSubsetAB = subset(setA, setB);
const isSubsetABC = subset(setA, setB, setC);
```

### proper subset: `A ⊂ B`
A set is a proper subset of another if all of its elements
are elements of the other set, and it has a lower cardinality
than the other set.

![proper subset visual][proper-subset-visual-url]
```typescript
import { properSubset } from 'set-utilities';

const isProperSubsetAB = properSubset(setA, setB);
const isProperSubsetABC = properSubset(setA, setB, setC);
```

### superset: `A ⊇ B`
A set is a superset of another if it contains all the elements
contained in the other set.
Expand All @@ -112,6 +125,19 @@ const isSupersetAB = superset(setA, setB);
const isSupersetABC = superset(setA, setB, setC);
```

### proper superset: `A ⊃ B`
A set is a superset of another if it contains all the elements
contained in the other set, and it has a greater cardinality
than the other set.

![proper superset visual][proper-superset-visual-url]
```typescript
import { properSuperset } from 'set-utilities';

const isProperSupersetAB = properSuperset(setA, setB);
const isProperSupersetABC = properSuperset(setA, setB, setC);
```


## Set Ordering:

Expand Down Expand Up @@ -154,4 +180,6 @@ const sortedByComparator = sort(setA, comparatorFunction);
[equivalence-visual-url]: https://github.com/kubikowski/set-utilities/wiki/assets/equivalence.svg
[disjoint-visual-url]: https://github.com/kubikowski/set-utilities/wiki/assets/disjoint.svg
[subset-visual-url]: https://github.com/kubikowski/set-utilities/wiki/assets/subset.svg
[proper-subset-visual-url]: https://github.com/kubikowski/set-utilities/wiki/assets/proper-subset.svg
[superset-visual-url]: https://github.com/kubikowski/set-utilities/wiki/assets/superset.svg
[proper-superset-visual-url]: https://github.com/kubikowski/set-utilities/wiki/assets/proper-superset.svg
2 changes: 1 addition & 1 deletion src/comparisons/equivalence.function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function equivalence<T, S extends ReadonlySet<T>>(...sets: S[]): boolean
}

const cardinalities = sets.map(set => set.size);
const primaryCardinality = cardinalities[0]!;
const primaryCardinality = cardinalities.shift()!;
const allSetsHaveEqualCardinalities = cardinalities
.every(cardinality => cardinality === primaryCardinality);

Expand Down
37 changes: 37 additions & 0 deletions src/comparisons/proper-subset.function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export function properSubset<T>(...sets: Set<T>[]): boolean;
export function properSubset<T>(...sets: ReadonlySet<T>[]): boolean;

/**
* A set is a proper subset of another if all of its elements
* are elements of the other set (and thereafter), and it has a
* greater cardinality than the other set (and thereafter).
*
* Proper subset is notated A ⊂ B,
* and not proper subset is A ⊄ B.
*
* @description A ⊂ B ⇔ (|A| < |B|) ∧ (∀x : (x ∈ A ⇒ x ∈ B))
*/
export function properSubset<T, S extends ReadonlySet<T>>(...sets: S[]): boolean {
if (sets.length < 2) {
return true;
}

const cardinalities = sets.map(set => set.size);
const primaryCardinality = cardinalities.shift()!;
const allSetsHaveGreaterCardinalities = cardinalities
.every(cardinality => cardinality > primaryCardinality);

if (!allSetsHaveGreaterCardinalities) {
return false;
}

for (const value of sets[0]!) {
for (let index = 1; index < sets.length; index++) {
if (!sets[index]?.has(value)) {
return false;
}
}
}

return true;
}
37 changes: 37 additions & 0 deletions src/comparisons/proper-superset.function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export function properSuperset<T>(...sets: Set<T>[]): boolean;
export function properSuperset<T>(...sets: ReadonlySet<T>[]): boolean;

/**
* A set is a proper subset of another if all of its elements
* are elements of the other set (and thereafter), and it has a
* greater cardinality than the other set (and thereafter).
*
* Proper superset is notated A ⊃ B,
* and not proper superset is A ⊅ B.
*
* @description A ⊃ B ⇔ (|A| > |B|) ∧ (∀x : (x ∈ B ⇒ x ∈ A))
*/
export function properSuperset<T, S extends ReadonlySet<T>>(...sets: S[]): boolean {
if (sets.length < 2) {
return true;
}

const cardinalities = sets.map(set => set.size);
const primaryCardinality = cardinalities.shift()!;
const allSetsHaveLesserCardinalities = cardinalities
.every(cardinality => cardinality < primaryCardinality);

if (!allSetsHaveLesserCardinalities) {
return false;
}

for (let index = 1; index < sets.length; index++) {
for (const value of sets[index]!) {
if (!sets[0]?.has(value)) {
return false;
}
}
}

return true;
}
4 changes: 1 addition & 3 deletions src/comparisons/subset.function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ export function subset<T>(...sets: ReadonlySet<T>[]): boolean;
*
* Subset is notated A ⊆ B,
* where not subset is A ⊈ B.
* Proper subset is notated A ⊂ B,
* and not proper subset is A ⊄ B.
*
* @description A ⊆ B ⇔ ∀x : (x ∈ A ⇒ x ∈ B)
*/
Expand All @@ -18,7 +16,7 @@ export function subset<T, S extends ReadonlySet<T>>(...sets: S[]): boolean {
}

const cardinalities = sets.map(set => set.size);
const primaryCardinality = cardinalities[0]!;
const primaryCardinality = cardinalities.shift()!;
const allSetsHaveGreaterCardinalities = cardinalities
.every(cardinality => cardinality >= primaryCardinality);

Expand Down
2 changes: 1 addition & 1 deletion src/comparisons/superset.function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function superset<T, S extends ReadonlySet<T>>(...sets: S[]): boolean {
}

const cardinalities = sets.map(set => set.size);
const primaryCardinality = cardinalities[0]!;
const primaryCardinality = cardinalities.shift()!;
const allSetsHaveLesserCardinalities = cardinalities
.every(cardinality => cardinality <= primaryCardinality);

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { disjoint } from './comparisons/disjoint.function';
export { equivalence } from './comparisons/equivalence.function';
export { properSubset } from './comparisons/proper-subset.function';
export { properSuperset } from './comparisons/proper-superset.function';
export { subset } from './comparisons/subset.function';
export { superset } from './comparisons/superset.function';

Expand Down
10 changes: 6 additions & 4 deletions test/comparisons/disjoint.function.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ describe('disjoint', () => {
expect(disjoint(setA, setB, setC)).toBe(false);
});

it('two sets with no shared values are disjoint', () => {
expect(disjoint(setA, setD)).toBe(true);
it('the empty set is disjoint with itself', () => {
expect(disjoint(empty, empty)).toBe(true);
});

/* custom disjoint tests */

it('any set and the empty set are disjoint', () => {
expect(disjoint(setA, empty)).toBe(true);
});

it('two of the empty set are disjoint', () => {
expect(disjoint(empty, empty)).toBe(true);
it('two sets with no shared values are disjoint', () => {
expect(disjoint(setA, setD)).toBe(true);
});
});
16 changes: 11 additions & 5 deletions test/comparisons/equivalence.function.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from '@jest/globals';
import { equivalence } from '../../src';
import { setA, setB, setC, universal } from '../constants/testing-constants';
import { empty, minimal, setA, setB, setC } from '../constants/testing-constants';

describe('equivalence', () => {
it('no sets are equivalent', () => {
Expand All @@ -19,15 +19,21 @@ describe('equivalence', () => {
expect(equivalence(setA, setA, setA)).toBe(true);
});

it('two sets with different cardinalities are not equivalent', () => {
expect(equivalence(setA, universal)).toBe(false);
});

it('two different sets are not equivalent', () => {
expect(equivalence(setA, setB)).toBe(false);
});

it('three different sets are not equivalent', () => {
expect(equivalence(setA, setB, setC)).toBe(false);
});

it('the empty set is equivalent to itself', () => {
expect(equivalence(empty, empty)).toBe(true);
});

/* custom equivalence tests */

it('two sets with different cardinalities are not equivalent', () => {
expect(equivalence(setA, minimal)).toBe(false);
});
});
51 changes: 51 additions & 0 deletions test/comparisons/proper-subset.function.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, expect, it } from '@jest/globals';
import { properSubset } from '../../src';
import {empty, minimal, setA, setB, setC, setD, universal} from '../constants/testing-constants';

describe('proper subset', () => {
it('no sets are proper subsets', () => {
expect(properSubset()).toBe(true);
});

it('single set is a proper subset', () => {
expect(properSubset(setA)).toBe(true);
});

it('same set is not a proper subset', () => {
expect(properSubset(setA, setA)).toBe(false);
});

it('many of the same set are not proper subsets', () => {
expect(properSubset(setA, setA, setA)).toBe(false);
});

it('two sets with different values are not proper subsets', () => {
expect(properSubset(setA, setB)).toBe(false);
});

it('three sets with different values are not proper subsets', () => {
expect(properSubset(setA, setB, setC)).toBe(false);
});

it('the empty set is not a proper subset of itself', () => {
expect(properSubset(empty, empty)).toBe(false);
});

/* custom proper subset tests */

it('following sets with lower cardinalities are not proper subsets', () => {
expect(properSubset(setA, minimal)).toBe(false);
});

it('sets without value bijection are not proper subsets', () => {
expect(properSubset(setD, setA)).toBe(false);
});

it('any non-universal set is a proper subset of the universal set', () => {
expect(properSubset(setA, universal)).toBe(true);
});

it('the empty set is a proper subset of every non-empty set', () => {
expect(properSubset(empty, minimal, setA, setB, setC, universal)).toBe(true);
});
});
51 changes: 51 additions & 0 deletions test/comparisons/proper-superset.function.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, expect, it } from '@jest/globals';
import { properSuperset } from '../../src';
import { empty, minimal, setA, setB, setC, setD, universal } from '../constants/testing-constants';

describe('proper superset', () => {
it('no sets are proper supersets', () => {
expect(properSuperset()).toBe(true);
});

it('single set is a proper superset', () => {
expect(properSuperset(setA)).toBe(true);
});

it('same set is not a proper superset', () => {
expect(properSuperset(setA, setA)).toBe(false);
});

it('many of the same set are not proper supersets', () => {
expect(properSuperset(setA, setA, setA)).toBe(false);
});

it('two sets with different values are not proper supersets', () => {
expect(properSuperset(setA, setB)).toBe(false);
});

it('three sets with different values are not proper supersets', () => {
expect(properSuperset(setA, setB, setC)).toBe(false);
});

it('the empty set is not a proper superset of itself', () => {
expect(properSuperset(empty, empty)).toBe(false);
});

/* custom proper superset tests */

it('following sets with greater cardinalities are not proper supersets', () => {
expect(properSuperset(minimal, setA)).toBe(false);
});

it('sets without value bijection are not proper supersets', () => {
expect(properSuperset(setA, setD)).toBe(false);
});

it('any non-empty set is a proper superset of the empty set', () => {
expect(properSuperset(setA, empty)).toBe(true);
});

it('the universal set is a proper superset of every non-universal set', () => {
expect(properSuperset(universal, setA, setB, setC, minimal, empty)).toBe(true);
});
});
16 changes: 13 additions & 3 deletions test/comparisons/subset.function.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from '@jest/globals';
import { subset } from '../../src';
import { empty, setA, setB, setC, universal } from '../constants/testing-constants';
import { empty, minimal, setA, setB, setC, setD, universal } from '../constants/testing-constants';

describe('subset', () => {
it('no sets are subsets', () => {
Expand All @@ -27,15 +27,25 @@ describe('subset', () => {
expect(subset(setA, setB, setC)).toBe(false);
});

it('the empty set is a subset of itself', () => {
expect(subset(empty, empty)).toBe(true);
});

/* custom subset tests */

it('following sets with lower cardinalities are not subsets', () => {
expect(subset(setA, empty)).toBe(false);
expect(subset(setA, minimal)).toBe(false);
});

it('sets without value bijection are not subsets', () => {
expect(subset(setD, setA)).toBe(false);
});

it('any set is a subset of the universal set', () => {
expect(subset(setA, universal)).toBe(true);
});

it('the empty set is a subset of every set', () => {
expect(subset(empty, setA, setB, setC, universal)).toBe(true);
expect(subset(empty, minimal, setA, setB, setC, universal)).toBe(true);
});
});
18 changes: 14 additions & 4 deletions test/comparisons/superset.function.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from '@jest/globals';
import { superset } from '../../src';
import { empty, setA, setB, setC, universal } from '../constants/testing-constants';
import {subset, superset} from '../../src';
import {empty, minimal, setA, setB, setC, setD, universal} from '../constants/testing-constants';

describe('superset', () => {
it('no sets are superset', () => {
Expand All @@ -27,15 +27,25 @@ describe('superset', () => {
expect(superset(setA, setB, setC)).toBe(false);
});

it('the empty set is a superset of itself', () => {
expect(superset(empty, empty)).toBe(true);
});

/* custom superset tests */

it('following sets with greater cardinalities are not supersets', () => {
expect(superset(setA, universal)).toBe(false);
expect(superset(minimal, setA)).toBe(false);
});

it('sets without value bijection are not supersets', () => {
expect(subset(setA, setD)).toBe(false);
});

it('any set is a superset of the empty set', () => {
expect(superset(setA, empty)).toBe(true);
});

it('the universal set is a superset of every set', () => {
expect(superset(universal, setA, setB, setC, empty)).toBe(true);
expect(superset(universal, setA, setB, setC, minimal, empty)).toBe(true);
});
});
Loading

0 comments on commit f984da5

Please sign in to comment.