Skip to content

Commit 90b3784

Browse files
authored
Implement Map.groupBy and Object.groupBy (#1546)
* Add support for Object.groupBy * Implement Map.groupBy
1 parent 587711b commit 90b3784

File tree

8 files changed

+156
-0
lines changed

8 files changed

+156
-0
lines changed

src/LuaLib.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export enum LuaLibFeature {
5858
Iterator = "Iterator",
5959
LuaIteratorSpread = "LuaIteratorSpread",
6060
Map = "Map",
61+
MapGroupBy = "MapGroupBy",
6162
Match = "Match",
6263
MathAtan2 = "MathAtan2",
6364
MathModf = "MathModf",
@@ -77,6 +78,7 @@ export enum LuaLibFeature {
7778
ObjectFromEntries = "ObjectFromEntries",
7879
ObjectGetOwnPropertyDescriptor = "ObjectGetOwnPropertyDescriptor",
7980
ObjectGetOwnPropertyDescriptors = "ObjectGetOwnPropertyDescriptors",
81+
ObjectGroupBy = "ObjectGroupBy",
8082
ObjectKeys = "ObjectKeys",
8183
ObjectRest = "ObjectRest",
8284
ObjectValues = "ObjectValues",

src/lualib/MapGroupBy.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export function __TS__MapGroupBy<K, T>(
2+
this: void,
3+
items: Iterable<T>,
4+
keySelector: (item: T, index: number) => K
5+
): Map<K, T[]> {
6+
const result = new Map<K, T[]>();
7+
8+
let i = 0;
9+
for (const item of items) {
10+
const key = keySelector(item, i);
11+
12+
if (result.has(key)) {
13+
result.get(key)!.push(item);
14+
} else {
15+
result.set(key, [item]);
16+
}
17+
18+
i++;
19+
}
20+
21+
return result;
22+
}

src/lualib/ObjectGroupBy.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export function __TS__ObjectGroupBy<K extends PropertyKey, T>(
2+
this: void,
3+
items: Iterable<T>,
4+
keySelector: (item: T, index: number) => K
5+
): Partial<Record<K, T[]>> {
6+
const result: Partial<Record<K, T[]>> = {};
7+
8+
let i = 0;
9+
for (const item of items) {
10+
const key = keySelector(item, i);
11+
12+
if (key in result) {
13+
result[key]!.push(item);
14+
} else {
15+
result[key] = [item];
16+
}
17+
18+
i++;
19+
}
20+
21+
return result;
22+
}

src/transformation/builtins/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { transformStringConstructorCall, transformStringProperty, transformStrin
1818
import { transformSymbolConstructorCall } from "./symbol";
1919
import { unsupportedBuiltinOptionalCall } from "../utils/diagnostics";
2020
import { LuaTarget } from "../../CompilerOptions";
21+
import { transformMapConstructorCall } from "./map";
2122

2223
export function transformBuiltinPropertyAccessExpression(
2324
context: TransformationContext,
@@ -93,6 +94,9 @@ function tryTransformBuiltinGlobalMethodCall(
9394
case "Console":
9495
result = transformConsoleCall(context, node, calledMethod);
9596
break;
97+
case "MapConstructor":
98+
result = transformMapConstructorCall(context, node, calledMethod);
99+
break;
96100
case "Math":
97101
result = transformMathCall(context, node, calledMethod);
98102
break;

src/transformation/builtins/map.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as lua from "../../LuaAST";
2+
import * as ts from "typescript";
3+
import { TransformationContext } from "../context";
4+
import { unsupportedProperty } from "../utils/diagnostics";
5+
import { transformArguments } from "../visitors/call";
6+
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
7+
8+
export function transformMapConstructorCall(
9+
context: TransformationContext,
10+
node: ts.CallExpression,
11+
calledMethod: ts.PropertyAccessExpression
12+
): lua.Expression | undefined {
13+
const args = transformArguments(context, node.arguments);
14+
const methodName = calledMethod.name.text;
15+
16+
switch (methodName) {
17+
case "groupBy":
18+
return transformLuaLibFunction(context, LuaLibFeature.MapGroupBy, node, ...args);
19+
default:
20+
context.diagnostics.push(unsupportedProperty(calledMethod.name, "Map", methodName));
21+
}
22+
}

src/transformation/builtins/object.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export function transformObjectConstructorCall(
2626
return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptor, node, ...args);
2727
case "getOwnPropertyDescriptors":
2828
return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptors, node, ...args);
29+
case "groupBy":
30+
return transformLuaLibFunction(context, LuaLibFeature.ObjectGroupBy, node, ...args);
2931
case "keys":
3032
return transformLuaLibFunction(context, LuaLibFeature.ObjectKeys, node, ...args);
3133
case "values":

test/unit/builtins/map.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,47 @@ describe.each(iterationMethods)("map.%s() preserves insertion order", iterationM
209209
`.expectToMatchJsResult();
210210
});
211211
});
212+
213+
describe("Map.groupBy", () => {
214+
test("empty", () => {
215+
util.testFunction`
216+
const array = [];
217+
218+
const map = Map.groupBy(array, (num, index) => {
219+
return num % 2 === 0 ? "even": "odd";
220+
});
221+
222+
return Object.fromEntries(map.entries());
223+
`.expectToEqual([]);
224+
});
225+
226+
test("groupBy", () => {
227+
util.testFunction`
228+
const array = [0, 1, 2, 3, 4, 5];
229+
230+
const map = Map.groupBy(array, (num, index) => {
231+
return num % 2 === 0 ? "even": "odd";
232+
});
233+
234+
return Object.fromEntries(map.entries());
235+
`.expectToEqual({
236+
even: [0, 2, 4],
237+
odd: [1, 3, 5],
238+
});
239+
});
240+
241+
test("groupBy index", () => {
242+
util.testFunction`
243+
const array = [0, 1, 2, 3, 4, 5];
244+
245+
const map = Map.groupBy(array, (num, index) => {
246+
return index < 3 ? "low": "high";
247+
});
248+
249+
return Object.fromEntries(map.entries());
250+
`.expectToEqual({
251+
low: [0, 1, 2],
252+
high: [3, 4, 5],
253+
});
254+
});
255+
});

test/unit/builtins/object.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,41 @@ describe("delete from object", () => {
264264
.expectToMatchJsResult();
265265
});
266266
});
267+
268+
describe("Object.groupBy", () => {
269+
test("empty", () => {
270+
util.testFunction`
271+
const array = [];
272+
273+
return Object.groupBy(array, (num, index) => {
274+
return num % 2 === 0 ? "even": "odd";
275+
});
276+
`.expectToEqual([]);
277+
});
278+
279+
test("groupBy", () => {
280+
util.testFunction`
281+
const array = [0, 1, 2, 3, 4, 5];
282+
283+
return Object.groupBy(array, (num, index) => {
284+
return num % 2 === 0 ? "even": "odd";
285+
});
286+
`.expectToEqual({
287+
even: [0, 2, 4],
288+
odd: [1, 3, 5],
289+
});
290+
});
291+
292+
test("groupBy index", () => {
293+
util.testFunction`
294+
const array = [0, 1, 2, 3, 4, 5];
295+
296+
return Object.groupBy(array, (num, index) => {
297+
return index < 3 ? "low": "high";
298+
});
299+
`.expectToEqual({
300+
low: [0, 1, 2],
301+
high: [3, 4, 5],
302+
});
303+
});
304+
});

0 commit comments

Comments
 (0)