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; },