Skip to content
Merged
1 change: 1 addition & 0 deletions src/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum LuaLibFeature {
ArrayMap = "ArrayMap",
ArrayPush = "ArrayPush",
ArrayReduce = "ArrayReduce",
ArrayReduceRight = "ArrayReduceRight",
ArrayReverse = "ArrayReverse",
ArrayShift = "ArrayShift",
ArrayUnshift = "ArrayUnshift",
Expand Down
2 changes: 2 additions & 0 deletions src/LuaTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5130,6 +5130,8 @@ export class LuaTransformer {
return this.transformLuaLibFunction(LuaLibFeature.ArrayFilter, node, caller, ...params);
case "reduce":
return this.transformLuaLibFunction(LuaLibFeature.ArrayReduce, node, caller, ...params);
case "reduceRight":
return this.transformLuaLibFunction(LuaLibFeature.ArrayReduceRight, node, caller, ...params);
case "some":
return this.transformLuaLibFunction(LuaLibFeature.ArraySome, node, caller, ...params);
case "every":
Expand Down
29 changes: 29 additions & 0 deletions src/lualib/ArrayReduceRight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.reduce
function __TS__ArrayReduceRight<T>(
this: void,
arr: T[],
callbackFn: (accumulator: T, currentValue: T, index: number, array: T[]) => T,
...initial: Vararg<T>
): T {
const len = arr.length;

let k = len - 1;
let accumulator = undefined;

// Check if initial value is present in function call
if (select("#", ...initial) !== 0) {
accumulator = select(1, ...initial);
} else if (len > 0) {
accumulator = arr[k];
k = k - 1;
} else {
// tslint:disable-next-line: no-string-throw
throw "Reduce of empty array with no initial value";
}

for (const i of forRange(k, 0, -1)) {
accumulator = callbackFn(accumulator, arr[i], i, arr);
}

return accumulator;
}
46 changes: 24 additions & 22 deletions test/unit/builtins/array.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,31 +507,33 @@ test.each([
util.testExpressionTemplate`${array}.flatMap(${map})`.expectToEqual(expected);
});

test.each<[[(total: number, currentItem: number, index: number, array: number[]) => number, number?]]>([
[[(total, currentItem) => total + currentItem]],
[[(total, currentItem) => total * currentItem]],
[[(total, currentItem) => total + currentItem, 10]],
[[(total, currentItem) => total * currentItem, 10]],
[[(total, _, index, array) => total + array[index]]],
[[(a, b) => a + b]],
])("array.reduce (%p)", args => {
util.testExpression`[1, 3, 5, 7].reduce(${util.formatCode(...args)})`.expectToMatchJsResult();
});
describe.each(["reduce", "reduceRight"])("array.%s", reduce => {
test.each<[[(total: number, currentItem: number, index: number, array: number[]) => number, number?]]>([
[[(total, currentItem) => total + currentItem]],
[[(total, currentItem) => total * currentItem]],
[[(total, currentItem) => total + currentItem, 10]],
[[(total, currentItem) => total * currentItem, 10]],
[[(total, _, index, array) => total + array[index]]],
[[(a, b) => a + b]],
])("usage (%p)", args => {
util.testExpression`[1, 3, 5, 7].${reduce}(${util.formatCode(...args)})`.expectToMatchJsResult();
});

test("array.reduce empty undefined initial", () => {
util.testExpression`[].reduce(() => {}, undefined)`.expectToMatchJsResult();
});
test("empty undefined initial", () => {
util.testExpression`[].${reduce}(() => {}, undefined)`.expectToMatchJsResult();
});

test("array.reduce empty no initial", () => {
util.testExpression`[].reduce(() => {})`.expectToMatchJsResult(true);
});
test("empty no initial", () => {
util.testExpression`[].${reduce}(() => {})`.expectToMatchJsResult(true);
});

test("array.reduce undefined returning callback", () => {
util.testFunction`
const calls: Array<{ a: void, b: string }> = [];
["a", "b"].reduce<void>((a, b) => { calls.push({ a, b }) }, undefined);
return calls;
`.expectToMatchJsResult();
test("undefined returning callback", () => {
util.testFunction`
const calls: Array<{ a: void, b: string }> = [];
["a", "b"].${reduce}<void>((a, b) => { calls.push({ a, b }) }, undefined);
return calls;
`.expectToMatchJsResult();
});
});

const genericChecks = [
Expand Down