From 8ef0d2ac835eb057443c3d96b375014085debab8 Mon Sep 17 00:00:00 2001 From: Weiller Carvalho Date: Sun, 16 Nov 2025 07:43:15 -0300 Subject: [PATCH] [BUGFIX beta] Align EmberArray.reduce with native semantics. --- .../runtime/tests/array/reduce-test.js | 15 +++++++++++ packages/@ember/array/index.ts | 25 ++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/@ember/-internals/runtime/tests/array/reduce-test.js b/packages/@ember/-internals/runtime/tests/array/reduce-test.js index cf549f7ce08..b85f08b98ab 100644 --- a/packages/@ember/-internals/runtime/tests/array/reduce-test.js +++ b/packages/@ember/-internals/runtime/tests/array/reduce-test.js @@ -8,6 +8,12 @@ class ReduceTests extends AbstractTestCase { this.assert.equal(res, 6); } + '@test uses the first item as the initial accumulator when not provided'() { + let obj = this.newObject([1, 2, 3]); + let res = obj.reduce((previousValue, item) => previousValue + item); + this.assert.equal(res, 6); + } + '@test passes index of item to callback'() { let obj = this.newObject([1, 2, 3]); let res = obj.reduce((previousValue, item, index) => previousValue + index, 0); @@ -19,6 +25,15 @@ class ReduceTests extends AbstractTestCase { let res = obj.reduce((previousValue, item, index, enumerable) => enumerable, 0); this.assert.equal(res, obj); } + + '@test throws when called on an empty array without an initial value'() { + let obj = this.newObject([]); + + this.assert.throws( + () => obj.reduce(() => 0), + /Reduce of empty array with no initial value/ + ); + } } runArrayTests('reduce', ReduceTests); diff --git a/packages/@ember/array/index.ts b/packages/@ember/array/index.ts index bdff2a18e4b..0d73dfb0ffe 100644 --- a/packages/@ember/array/index.ts +++ b/packages/@ember/array/index.ts @@ -1380,19 +1380,32 @@ const EmberArray = Mixin.create(Enumerable, { return any(this, callback); }, - // FIXME: When called without initialValue, behavior does not match native behavior reduce( this: EmberArray, callback: (summation: V, current: T, index: number, arr: EmberArray) => V, - initialValue: V + initialValue?: V ) { assert('`reduce` expects a function as first argument.', typeof callback === 'function'); - let ret = initialValue; + let length = this.length; + let startIndex = 0; + let ret: V; + + if (arguments.length > 1) { + ret = initialValue as V; + } else { + if (length === 0) { + throw new TypeError('Reduce of empty array with no initial value'); + } + + ret = objectAt(this, 0) as unknown as V; + startIndex = 1; + } - this.forEach(function (item, i) { - ret = callback(ret, item, i, this); - }, this); + for (let index = startIndex; index < length; index++) { + let item = objectAt(this, index) as T; + ret = callback(ret, item, index, this); + } return ret; },