Add `MapUtil.merge` for combining maps (#794)
See the docstring for details. Test plan: Unit tests.
This commit is contained in:
parent
513820c177
commit
417265a4d4
|
@ -122,3 +122,27 @@ export function mapEntries<K, V, InK, InV>(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge maps without mutating the arguments.
|
||||
*
|
||||
* Merges multiple maps, returning a new map which has every key from
|
||||
* the source maps, with their corresponding values. None of the inputs
|
||||
* are mutated. In the event that multiple maps have the same key, an
|
||||
* error will be thrown.
|
||||
*/
|
||||
export function merge<K, V>(
|
||||
maps: $ReadOnlyArray<Map<$Subtype<K>, $Subtype<V>>>
|
||||
): Map<K, V> {
|
||||
const result = new Map();
|
||||
let updates = 0;
|
||||
for (const map of maps) {
|
||||
for (const [key, value] of map.entries()) {
|
||||
result.set(key, value);
|
||||
if (result.size !== ++updates) {
|
||||
throw new Error(`Maps have duplicate key: ${String(key)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -245,4 +245,53 @@ describe("util/map", () => {
|
|||
expect(output).toEqual(new Map().set(11, "wat").set(12, "wat"));
|
||||
});
|
||||
});
|
||||
describe("merge", () => {
|
||||
it("combines two simple maps", () => {
|
||||
const a = new Map().set("a", 1);
|
||||
const b = new Map().set("b", 2);
|
||||
const c = new Map().set("c", 3);
|
||||
expect(MapUtil.merge([a, b, c])).toEqual(
|
||||
new Map()
|
||||
.set("a", 1)
|
||||
.set("b", 2)
|
||||
.set("c", 3)
|
||||
);
|
||||
});
|
||||
it("treats empty map as an identity", () => {
|
||||
const m = new Map().set("a", 11).set("b", 22);
|
||||
expect(MapUtil.merge([new Map(), m, new Map()])).toEqual(m);
|
||||
});
|
||||
it("errors if there are any duplicate keys", () => {
|
||||
const a = new Map().set("a", null);
|
||||
expect(() => MapUtil.merge([a, a])).toThrowError("duplicate key");
|
||||
});
|
||||
it("handles null and undefined appropriately", () => {
|
||||
const a = new Map().set(undefined, undefined);
|
||||
const b = new Map().set(null, null);
|
||||
expect(MapUtil.merge([a, b])).toEqual(
|
||||
new Map().set(undefined, undefined).set(null, null)
|
||||
);
|
||||
});
|
||||
it("merge works on empty list", () => {
|
||||
expect(MapUtil.merge([])).toEqual(new Map());
|
||||
});
|
||||
it("allows upcasting the type parameters", () => {
|
||||
const numberMap: Map<number, number> = new Map().set(1, 2);
|
||||
const stringMap: Map<string, string> = new Map().set("one", "two");
|
||||
type NS = number | string;
|
||||
const _unused_polyMap: Map<NS, NS> = MapUtil.merge([
|
||||
numberMap,
|
||||
stringMap,
|
||||
]);
|
||||
});
|
||||
it("produces expected type errors", () => {
|
||||
const numberMap: Map<number, number> = new Map().set(1, 2);
|
||||
const stringMap: Map<string, string> = new Map().set("one", "two");
|
||||
// $ExpectFlowError
|
||||
const _unused_badMap: Map<string, number> = MapUtil.merge([
|
||||
numberMap,
|
||||
stringMap,
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue