Skip to content

Commit 6ffb500

Browse files
authored
feat!: improve semantics of map.intersection and map.difference (#22)
1 parent 399dd1b commit 6ffb500

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

src/__tests__/map.test.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ describe('DeepMap', () => {
259259
});
260260

261261
describe('union method', () => {
262-
it('the union of two maps contains all unique keys and associated values', async () => {
262+
it('the union of two maps contains all key-value pairs from first map and any unique keys from second map', async () => {
263263
const map1 = new DeepMap([
264264
[new K('1'), new V('1')],
265265
[new K('2'), new V('2')],
@@ -293,19 +293,19 @@ describe('DeepMap', () => {
293293
});
294294

295295
describe('intersection method', () => {
296-
it('the intersection of two maps contains all shared keys and associated values', async () => {
296+
it('the intersection of two maps contains all shared key-value pairs', async () => {
297297
const map1 = new DeepMap([
298298
[new K('1'), new V('1')],
299299
[new K('2'), new V('2')],
300+
[new K('3'), new V('3')],
300301
]);
301302
const map2 = new DeepMap([
302-
[new K('2'), new V('999')],
303-
[new K('3'), new V('3')],
303+
[new K('1'), new V('1')],
304+
[new K('2'), new V('999')], // same key but different value -- not a match
305+
[new K('4'), new V('4')],
304306
]);
305307
const intersectionMap = map1.intersection(map2);
306-
expect([...intersectionMap.entries()]).toStrictEqual([
307-
[new K('2'), new V('2')], // value for common key is the value from the caller
308-
]);
308+
expect([...intersectionMap.entries()]).toStrictEqual([[new K('1'), new V('1')]]);
309309
});
310310

311311
it('the intersection of equal maps is the same as either input', async () => {
@@ -325,18 +325,21 @@ describe('DeepMap', () => {
325325
});
326326

327327
describe('difference method', () => {
328-
it('the difference of two maps contains all keys from first map not in second', async () => {
328+
it('the difference of two maps contains all key-value pairs from first map not in second', async () => {
329329
const map1 = new DeepMap([
330330
[new K('1'), new V('1')],
331331
[new K('2'), new V('2')],
332+
[new K('3'), new V('3')],
332333
]);
333334
const map2 = new DeepMap([
334-
[new K('2'), new V('999')],
335-
[new K('3'), new V('3')],
335+
[new K('1'), new V('1')],
336+
[new K('2'), new V('999')], // same key but different value -- not a match
337+
[new K('4'), new V('4')],
336338
]);
337339
const differenceMap = map1.difference(map2);
338340
expect([...differenceMap.entries()]).toStrictEqual([
339-
[new K('1'), new V('1')], // value for common key is the value from the caller
341+
[new K('2'), new V('2')],
342+
[new K('3'), new V('3')],
340343
]);
341344
});
342345

src/map.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -125,44 +125,50 @@ export class DeepMap<K, V, TxK = K, TxV = V> extends Map<K, V> implements Compar
125125
}
126126

127127
/**
128+
* @param other the map to compare against
128129
* @returns true if the entries of `other` are the same as this map
129130
*/
130131
equals(other: this): boolean {
131132
return this.size === other.size && this.contains(other);
132133
}
133134

134135
/**
136+
* @param other the map to compare against
135137
* @returns true if the entries of `other` are all contained in this map
136138
*/
137139
contains(other: this): boolean {
138-
return [...other.entries()].every(([otherKey, otherVal]) => {
139-
const thisVal = this.get(otherKey);
140-
return thisVal !== undefined && this.normalizeValue(thisVal) === this.normalizeValue(otherVal);
141-
});
140+
return [...other.entries()].every(([key, val]) => this.keyValuePairIsPresentIn(key, val, this));
142141
}
143142

144143
/**
144+
* @param other the map to compare against
145145
* @returns a new map whose keys are the union of keys between `this` and `other` maps.
146146
*
147-
* NOTE: If both maps prescribe the same key, the value from `this` will be retained.
147+
* NOTE: If both maps prescribe the same key, the key-value pair from `this` will be retained.
148148
*/
149149
union(other: this): DeepMap<K, V, TxK, TxV> {
150150
return new DeepMap([...other.entries(), ...this.entries()], this.options);
151151
}
152152

153153
/**
154-
* @returns a new map containing all key-value pairs in `this` whose keys are also in `other`.
154+
* @param other the map to compare against
155+
* @returns a new map containing all key-value pairs in `this` that are also present in `other`.
155156
*/
156157
intersection(other: this): DeepMap<K, V, TxK, TxV> {
157-
const intersectingPairs = [...this.entries()].filter(([key, _value]) => other.has(key));
158+
const intersectingPairs = [...this.entries()].filter(([key, val]) =>
159+
this.keyValuePairIsPresentIn(key, val, other)
160+
);
158161
return new DeepMap(intersectingPairs, this.options);
159162
}
160163

161164
/**
162-
* @returns a new map containing all key-value pairs in `this` whose keys are not also in `other`.
165+
* @param other the map to compare against
166+
* @returns a new map containing all key-value pairs in `this` that are not present in `other`.
163167
*/
164168
difference(other: this): DeepMap<K, V, TxK, TxV> {
165-
const differencePairs = [...this.entries()].filter(([key, _value]) => !other.has(key));
169+
const differencePairs = [...this.entries()].filter(
170+
([key, val]) => !this.keyValuePairIsPresentIn(key, val, other)
171+
);
166172
return new DeepMap(differencePairs, this.options);
167173
}
168174

@@ -175,4 +181,12 @@ export class DeepMap<K, V, TxK = K, TxV = V> extends Map<K, V> implements Compar
175181
protected normalizeValue(input: V): Normalized<TxV> {
176182
return this.normalizer.normalizeValue(input);
177183
}
184+
185+
/**
186+
* @returns true if the key is present in the provided map w/ the specified value
187+
*/
188+
private keyValuePairIsPresentIn(key: K, val: V, mapToCheck: this): boolean {
189+
const checkVal = mapToCheck.get(key);
190+
return checkVal !== undefined && this.normalizeValue(checkVal) === this.normalizeValue(val);
191+
}
178192
}

0 commit comments

Comments
 (0)