Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export enum LuaLibFeature {
Iterator = "Iterator",
LuaIteratorSpread = "LuaIteratorSpread",
Map = "Map",
MapGroupBy = "MapGroupBy",
Match = "Match",
MathAtan2 = "MathAtan2",
MathModf = "MathModf",
Expand All @@ -77,6 +78,7 @@ export enum LuaLibFeature {
ObjectFromEntries = "ObjectFromEntries",
ObjectGetOwnPropertyDescriptor = "ObjectGetOwnPropertyDescriptor",
ObjectGetOwnPropertyDescriptors = "ObjectGetOwnPropertyDescriptors",
ObjectGroupBy = "ObjectGroupBy",
ObjectKeys = "ObjectKeys",
ObjectRest = "ObjectRest",
ObjectValues = "ObjectValues",
Expand Down
22 changes: 22 additions & 0 deletions src/lualib/MapGroupBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function __TS__MapGroupBy<K, T>(
this: void,
items: Iterable<T>,
keySelector: (item: T, index: number) => K
): Map<K, T[]> {
const result = new Map<K, T[]>();

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;
}
22 changes: 22 additions & 0 deletions src/lualib/ObjectGroupBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function __TS__ObjectGroupBy<K extends PropertyKey, T>(
this: void,
items: Iterable<T>,
keySelector: (item: T, index: number) => K
): Partial<Record<K, T[]>> {
const result: Partial<Record<K, T[]>> = {};

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;
}
4 changes: 4 additions & 0 deletions src/transformation/builtins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
22 changes: 22 additions & 0 deletions src/transformation/builtins/map.ts
Original file line number Diff line number Diff line change
@@ -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));
}
}
2 changes: 2 additions & 0 deletions src/transformation/builtins/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down
44 changes: 44 additions & 0 deletions test/unit/builtins/map.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
});
});
});
38 changes: 38 additions & 0 deletions test/unit/builtins/object.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
});
});
});