From b5e5b9bdd0dff848ba8fe8053d85683ca4dd782a Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 17 Mar 2024 15:40:32 +0100 Subject: [PATCH 1/2] Add support for Object.groupBy --- src/LuaLib.ts | 2 ++ src/lualib/MapGroupBy.ts | 7 +++++ src/lualib/ObjectGroupBy.ts | 22 ++++++++++++++++ src/transformation/builtins/object.ts | 2 ++ test/unit/builtins/object.spec.ts | 38 +++++++++++++++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 src/lualib/MapGroupBy.ts create mode 100644 src/lualib/ObjectGroupBy.ts diff --git a/src/LuaLib.ts b/src/LuaLib.ts index a3d6d0f16..d051c60b4 100644 --- a/src/LuaLib.ts +++ b/src/LuaLib.ts @@ -58,6 +58,7 @@ export enum LuaLibFeature { Iterator = "Iterator", LuaIteratorSpread = "LuaIteratorSpread", Map = "Map", + MapGroupBy = "MapGroupBy", Match = "Match", MathAtan2 = "MathAtan2", MathModf = "MathModf", @@ -77,6 +78,7 @@ export enum LuaLibFeature { ObjectFromEntries = "ObjectFromEntries", ObjectGetOwnPropertyDescriptor = "ObjectGetOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptors = "ObjectGetOwnPropertyDescriptors", + ObjectGroupBy = "ObjectGroupBy", ObjectKeys = "ObjectKeys", ObjectRest = "ObjectRest", ObjectValues = "ObjectValues", diff --git a/src/lualib/MapGroupBy.ts b/src/lualib/MapGroupBy.ts new file mode 100644 index 000000000..56b68d815 --- /dev/null +++ b/src/lualib/MapGroupBy.ts @@ -0,0 +1,7 @@ +export function __TS__MapGroupBy( + this: void, + items: Iterable, + keySelector: (item: T, index: number) => K +): Map { + return new Map(); +} diff --git a/src/lualib/ObjectGroupBy.ts b/src/lualib/ObjectGroupBy.ts new file mode 100644 index 000000000..6328ad2aa --- /dev/null +++ b/src/lualib/ObjectGroupBy.ts @@ -0,0 +1,22 @@ +export function __TS__ObjectGroupBy( + this: void, + items: Iterable, + keySelector: (item: T, index: number) => K +): Partial> { + const result: Partial> = {}; + + let i = 0; + for (const item of items) { + const key = keySelector(item, i); + + if (key in result) { + result[key]!.push(item); + } else { + result[key] = [item]; + } + + i++; + } + + return result; +} diff --git a/src/transformation/builtins/object.ts b/src/transformation/builtins/object.ts index cd386ce19..43dd8a165 100644 --- a/src/transformation/builtins/object.ts +++ b/src/transformation/builtins/object.ts @@ -26,6 +26,8 @@ export function transformObjectConstructorCall( return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptor, node, ...args); case "getOwnPropertyDescriptors": return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptors, node, ...args); + case "groupBy": + return transformLuaLibFunction(context, LuaLibFeature.ObjectGroupBy, node, ...args); case "keys": return transformLuaLibFunction(context, LuaLibFeature.ObjectKeys, node, ...args); case "values": diff --git a/test/unit/builtins/object.spec.ts b/test/unit/builtins/object.spec.ts index 9cb5f8de3..1edc8a9d6 100644 --- a/test/unit/builtins/object.spec.ts +++ b/test/unit/builtins/object.spec.ts @@ -264,3 +264,41 @@ describe("delete from object", () => { .expectToMatchJsResult(); }); }); + +describe("Object.groupBy", () => { + test("empty", () => { + util.testFunction` + const array = []; + + return Object.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + `.expectToEqual([]); + }); + + test("groupBy", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + return Object.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + `.expectToEqual({ + even: [0, 2, 4], + odd: [1, 3, 5], + }); + }); + + test("groupBy index", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + return Object.groupBy(array, (num, index) => { + return index < 3 ? "low": "high"; + }); + `.expectToEqual({ + low: [0, 1, 2], + high: [3, 4, 5], + }); + }); +}); From 221081655934f841b6d998a266a6b7f7f3a22948 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 17 Mar 2024 15:52:09 +0100 Subject: [PATCH 2/2] Implement Map.groupBy --- src/lualib/MapGroupBy.ts | 17 ++++++++++- src/transformation/builtins/index.ts | 4 +++ src/transformation/builtins/map.ts | 22 ++++++++++++++ test/unit/builtins/map.spec.ts | 44 ++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/transformation/builtins/map.ts diff --git a/src/lualib/MapGroupBy.ts b/src/lualib/MapGroupBy.ts index 56b68d815..d3e079192 100644 --- a/src/lualib/MapGroupBy.ts +++ b/src/lualib/MapGroupBy.ts @@ -3,5 +3,20 @@ export function __TS__MapGroupBy( items: Iterable, keySelector: (item: T, index: number) => K ): Map { - return new Map(); + const result = new Map(); + + let i = 0; + for (const item of items) { + const key = keySelector(item, i); + + if (result.has(key)) { + result.get(key)!.push(item); + } else { + result.set(key, [item]); + } + + i++; + } + + return result; } diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index b7528cf6e..867942064 100644 --- a/src/transformation/builtins/index.ts +++ b/src/transformation/builtins/index.ts @@ -18,6 +18,7 @@ import { transformStringConstructorCall, transformStringProperty, transformStrin import { transformSymbolConstructorCall } from "./symbol"; import { unsupportedBuiltinOptionalCall } from "../utils/diagnostics"; import { LuaTarget } from "../../CompilerOptions"; +import { transformMapConstructorCall } from "./map"; export function transformBuiltinPropertyAccessExpression( context: TransformationContext, @@ -93,6 +94,9 @@ function tryTransformBuiltinGlobalMethodCall( case "Console": result = transformConsoleCall(context, node, calledMethod); break; + case "MapConstructor": + result = transformMapConstructorCall(context, node, calledMethod); + break; case "Math": result = transformMathCall(context, node, calledMethod); break; diff --git a/src/transformation/builtins/map.ts b/src/transformation/builtins/map.ts new file mode 100644 index 000000000..556578591 --- /dev/null +++ b/src/transformation/builtins/map.ts @@ -0,0 +1,22 @@ +import * as lua from "../../LuaAST"; +import * as ts from "typescript"; +import { TransformationContext } from "../context"; +import { unsupportedProperty } from "../utils/diagnostics"; +import { transformArguments } from "../visitors/call"; +import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; + +export function transformMapConstructorCall( + context: TransformationContext, + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +): lua.Expression | undefined { + const args = transformArguments(context, node.arguments); + const methodName = calledMethod.name.text; + + switch (methodName) { + case "groupBy": + return transformLuaLibFunction(context, LuaLibFeature.MapGroupBy, node, ...args); + default: + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Map", methodName)); + } +} diff --git a/test/unit/builtins/map.spec.ts b/test/unit/builtins/map.spec.ts index 7eec8844d..f2e1cbfbe 100644 --- a/test/unit/builtins/map.spec.ts +++ b/test/unit/builtins/map.spec.ts @@ -209,3 +209,47 @@ describe.each(iterationMethods)("map.%s() preserves insertion order", iterationM `.expectToMatchJsResult(); }); }); + +describe("Map.groupBy", () => { + test("empty", () => { + util.testFunction` + const array = []; + + const map = Map.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + + return Object.fromEntries(map.entries()); + `.expectToEqual([]); + }); + + test("groupBy", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + const map = Map.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + + return Object.fromEntries(map.entries()); + `.expectToEqual({ + even: [0, 2, 4], + odd: [1, 3, 5], + }); + }); + + test("groupBy index", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + const map = Map.groupBy(array, (num, index) => { + return index < 3 ? "low": "high"; + }); + + return Object.fromEntries(map.entries()); + `.expectToEqual({ + low: [0, 1, 2], + high: [3, 4, 5], + }); + }); +});