diff --git a/packages/collection/__tests__/collection.test.ts b/packages/collection/__tests__/collection.test.ts index 94be55336..927a1c466 100644 --- a/packages/collection/__tests__/collection.test.ts +++ b/packages/collection/__tests__/collection.test.ts @@ -116,16 +116,15 @@ describe('combineEntries() tests', () => { }); describe('difference() tests', () => { - const coll1 = createCollectionFrom(['a', 1], ['b', 2]); - const coll2 = createTestCollection(); - const diff = createCollectionFrom(['c', 3]); + const coll1 = createCollectionFrom(['a', 1], ['b', 2], ['c', 3]); + const coll2 = createCollectionFrom(['b', 2], ['d', 4], ['e', 5]); - test('it removes entries from the bigger collection on the right', () => { - expect(coll1.difference(coll2)).toStrictEqual(diff); + test('it returns the difference the collections', () => { + expect(coll1.difference(coll2)).toStrictEqual(createCollectionFrom(['a', 1], ['c', 3])); }); - test('removes the difference from the bigger collection on the left', () => { - expect(coll2.difference(coll1)).toStrictEqual(diff); + test('it returns the difference the collections from the opposite order', () => { + expect(coll2.difference(coll1)).toStrictEqual(createCollectionFrom(['d', 4], ['e', 5])); }); }); @@ -407,12 +406,12 @@ describe('hasAny() tests', () => { }); }); -describe('intersect() tests', () => { +describe('intersection() tests', () => { const coll1 = createCollectionFrom(['a', 1], ['b', 2]); const coll2 = createCollectionFrom(['a', 1], ['c', 3]); - test('it returns a new collection', () => { - const c = coll1.intersect(coll2); + test('it returns the intersection of the collections', () => { + const c = coll1.intersection(coll2); expect(c).toBeInstanceOf(Collection); expect(c.size).toStrictEqual(1); @@ -776,19 +775,6 @@ describe('sort() tests', () => { }); }); -describe('subtract() tests', () => { - const coll1 = createCollectionFrom(['a', 1], ['b', 2], ['c', 3], ['d', undefined]); - const coll2 = createCollectionFrom(['b', 2], ['c', 0]); - - test('it returns a new collection', () => { - const c = coll1.subtract(coll2); - expect(c).toBeInstanceOf(Collection); - expect(c.size).toStrictEqual(3); - - expect(c).toStrictEqual(createCollectionFrom(['a', 1], ['c', 3], ['d', undefined])); - }); -}); - describe('sweep() test', () => { const coll = createTestCollection(); @@ -816,6 +802,17 @@ describe('sweep() test', () => { }); }); +describe('symmetricDifference() tests', () => { + const coll1 = createCollectionFrom(['a', 1], ['b', 2], ['c', 3]); + const coll2 = createCollectionFrom(['b', 2], ['d', 4], ['e', 5]); + + test('it returns the symmetric difference of the collections', () => { + expect(coll1.symmetricDifference(coll2)).toStrictEqual( + createCollectionFrom(['a', 1], ['c', 3], ['d', 4], ['e', 5]), + ); + }); +}); + describe('tap() tests', () => { const coll = createTestCollection(); @@ -845,6 +842,19 @@ describe('toJSON() tests', () => { }); }); +describe('union() tests', () => { + const coll1 = createCollectionFrom(['a', 1], ['b', 2]); + const coll2 = createCollectionFrom(['a', 1], ['c', 3]); + + test('it returns the union of the collections', () => { + const c = coll1.union(coll2); + expect(c).toBeInstanceOf(Collection); + expect(c.size).toStrictEqual(3); + + expect(c).toStrictEqual(createCollectionFrom(['a', 1], ['b', 2], ['c', 3])); + }); +}); + describe('random thisArg tests', () => { const coll = createCollectionFrom(['a', 3], ['b', 2], ['c', 1]) as Collection; diff --git a/packages/collection/src/collection.ts b/packages/collection/src/collection.ts index 4f54dc2f4..6d2b94d05 100644 --- a/packages/collection/src/collection.ts +++ b/packages/collection/src/collection.ts @@ -751,48 +751,71 @@ export class Collection extends Map { } /** - * The intersect method returns a new structure containing items where the keys and values are present in both original structures. + * The intersection method returns a new collection containing the items where the key is present in both collections. * * @param other - The other Collection to filter against + * @example + * ```ts + * const col1 = new Collection([['a', 1], ['b', 2]]); + * const col2 = new Collection([['a', 1], ['c', 3]]); + * const intersection = col1.intersection(col2); + * console.log(col1.intersection(col2)); + * // => Collection { 'a' => 1 } + * ``` */ - public intersect(other: ReadonlyCollection): Collection { - const coll = new this.constructor[Symbol.species](); - for (const [key, value] of other) { - if (this.has(key) && Object.is(value, this.get(key))) { - coll.set(key, value); - } - } - - return coll; - } - - /** - * The subtract method returns a new structure containing items where the keys and values of the original structure are not present in the other. - * - * @param other - The other Collection to filter against - */ - public subtract(other: ReadonlyCollection): Collection { - const coll = new this.constructor[Symbol.species](); - for (const [key, value] of this) { - if (!other.has(key) || !Object.is(value, other.get(key))) { - coll.set(key, value); - } - } - - return coll; - } - - /** - * The difference method returns a new structure containing items where the key is present in one of the original structures but not the other. - * - * @param other - The other Collection to filter against - */ - public difference(other: ReadonlyCollection): Collection { + public intersection(other: ReadonlyCollection): Collection { const coll = new this.constructor[Symbol.species](); - for (const [key, value] of other) { - if (!this.has(key)) coll.set(key, value); + + for (const [key, value] of this) { + if (other.has(key)) coll.set(key, value); } + return coll; + } + + /** + * Returns a new collection containing the items where the key is present in either of the collections. + * + * @remarks + * + * If the collections have any items with the same key, the value from the first collection will be used. + * @param other - The other Collection to filter against + * @example + * ```ts + * const col1 = new Collection([['a', 1], ['b', 2]]); + * const col2 = new Collection([['a', 1], ['b', 3], ['c', 3]]); + * const union = col1.union(col2); + * console.log(union); + * // => Collection { 'a' => 1, 'b' => 2, 'c' => 3 } + * ``` + */ + public union(other: ReadonlyCollection): Collection { + const coll = new this.constructor[Symbol.species](this); + + for (const [key, value] of other) { + if (!coll.has(key)) coll.set(key, value); + } + + return coll; + } + + /** + * Returns a new collection containing the items where the key is present in this collection but not the other. + * + * @param other - The other Collection to filter against + * @example + * ```ts + * const col1 = new Collection([['a', 1], ['b', 2]]); + * const col2 = new Collection([['a', 1], ['c', 3]]); + * console.log(col1.difference(col2)); + * // => Collection { 'b' => 2 } + * console.log(col2.difference(col1)); + * // => Collection { 'c' => 3 } + * ``` + */ + public difference(other: ReadonlyCollection): Collection { + const coll = new this.constructor[Symbol.species](); + for (const [key, value] of this) { if (!other.has(key)) coll.set(key, value); } @@ -800,6 +823,33 @@ export class Collection extends Map { return coll; } + /** + * Returns a new collection containing only the items where the keys are present in either collection, but not both. + * + * @param other - The other Collection to filter against + * @example + * ```ts + * const col1 = new Collection([['a', 1], ['b', 2]]); + * const col2 = new Collection([['a', 1], ['c', 3]]); + * const symmetricDifference = col1.symmetricDifference(col2); + * console.log(col1.symmetricDifference(col2)); + * // => Collection { 'b' => 2, 'c' => 3 } + * ``` + */ + public symmetricDifference(other: ReadonlyCollection): Collection { + const coll = new this.constructor[Symbol.species](); + + for (const [key, value] of this) { + if (!other.has(key)) coll.set(key, value); + } + + for (const [key, value] of other) { + if (!this.has(key)) coll.set(key, value); + } + + return coll; + } + /** * Merges two Collections together into a new Collection. *