Skip to content

Commit e59ae4c

Browse files
Refactor out AccumulatorMap from groupBy (#3568)
1 parent d3d68e8 commit e59ae4c

File tree

4 files changed

+62
-18
lines changed

4 files changed

+62
-18
lines changed

src/execution/collectFields.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AccumulatorMap } from '../jsutils/AccumulatorMap';
12
import type { ObjMap } from '../jsutils/ObjMap';
23

34
import type {
@@ -37,7 +38,7 @@ export function collectFields(
3738
runtimeType: GraphQLObjectType,
3839
selectionSet: SelectionSetNode,
3940
): Map<string, ReadonlyArray<FieldNode>> {
40-
const fields = new Map();
41+
const fields = new AccumulatorMap<string, FieldNode>();
4142
collectFieldsImpl(
4243
schema,
4344
fragments,
@@ -67,7 +68,7 @@ export function collectSubfields(
6768
returnType: GraphQLObjectType,
6869
fieldNodes: ReadonlyArray<FieldNode>,
6970
): Map<string, ReadonlyArray<FieldNode>> {
70-
const subFieldNodes = new Map();
71+
const subFieldNodes = new AccumulatorMap<string, FieldNode>();
7172
const visitedFragmentNames = new Set<string>();
7273
for (const node of fieldNodes) {
7374
if (node.selectionSet) {
@@ -91,7 +92,7 @@ function collectFieldsImpl(
9192
variableValues: { [variable: string]: unknown },
9293
runtimeType: GraphQLObjectType,
9394
selectionSet: SelectionSetNode,
94-
fields: Map<string, Array<FieldNode>>,
95+
fields: AccumulatorMap<string, FieldNode>,
9596
visitedFragmentNames: Set<string>,
9697
): void {
9798
for (const selection of selectionSet.selections) {
@@ -100,13 +101,7 @@ function collectFieldsImpl(
100101
if (!shouldIncludeNode(variableValues, selection)) {
101102
continue;
102103
}
103-
const name = getFieldEntryKey(selection);
104-
const fieldList = fields.get(name);
105-
if (fieldList !== undefined) {
106-
fieldList.push(selection);
107-
} else {
108-
fields.set(name, [selection]);
109-
}
104+
fields.add(getFieldEntryKey(selection), selection);
110105
break;
111106
}
112107
case Kind.INLINE_FRAGMENT: {

src/jsutils/AccumulatorMap.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* ES6 Map with additional `add` method to accumulate items.
3+
*/
4+
export class AccumulatorMap<K, T> extends Map<K, Array<T>> {
5+
get [Symbol.toStringTag]() {
6+
return 'AccumulatorMap';
7+
}
8+
9+
add(key: K, item: T): void {
10+
const group = this.get(key);
11+
if (group === undefined) {
12+
this.set(key, [item]);
13+
} else {
14+
group.push(item);
15+
}
16+
}
17+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { AccumulatorMap } from '../AccumulatorMap';
5+
6+
function expectMap<K, V>(map: Map<K, V>) {
7+
return expect(Object.fromEntries(map.entries()));
8+
}
9+
10+
describe('AccumulatorMap', () => {
11+
it('can be Object.toStringified', () => {
12+
const accumulatorMap = new AccumulatorMap();
13+
14+
expect(Object.prototype.toString.call(accumulatorMap)).to.equal(
15+
'[object AccumulatorMap]',
16+
);
17+
});
18+
19+
it('accumulate items', () => {
20+
const accumulatorMap = new AccumulatorMap<string, number>();
21+
22+
expectMap(accumulatorMap).to.deep.equal({});
23+
24+
accumulatorMap.add('a', 1);
25+
accumulatorMap.add('b', 2);
26+
accumulatorMap.add('c', 3);
27+
accumulatorMap.add('b', 4);
28+
accumulatorMap.add('c', 5);
29+
accumulatorMap.add('c', 6);
30+
expectMap(accumulatorMap).to.deep.equal({
31+
a: [1],
32+
b: [2, 4],
33+
c: [3, 5, 6],
34+
});
35+
});
36+
});

src/jsutils/groupBy.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1+
import { AccumulatorMap } from './AccumulatorMap';
2+
13
/**
24
* Groups array items into a Map, given a function to produce grouping key.
35
*/
46
export function groupBy<K, T>(
57
list: ReadonlyArray<T>,
68
keyFn: (item: T) => K,
79
): Map<K, ReadonlyArray<T>> {
8-
const result = new Map<K, Array<T>>();
10+
const result = new AccumulatorMap<K, T>();
911
for (const item of list) {
10-
const key = keyFn(item);
11-
const group = result.get(key);
12-
if (group === undefined) {
13-
result.set(key, [item]);
14-
} else {
15-
group.push(item);
16-
}
12+
result.add(keyFn(item), item);
1713
}
1814
return result;
1915
}

0 commit comments

Comments
 (0)