Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

James Robb
James Robb

Posted on • Edited on

     

Array reduce

A reducer is a function that takes a collection and for each item in the collection, returns a new state. Most commonly we can use reducers to transform an old state of something to a new state of something. That could be an array to integer, array to array, array of objects representing application state to a new array of objects with the updated application state, anything really.

In most implementations the reduce function relies on 3 key components being available. Firstly is the collection to be reduced, secondly is the reducer function to run for each item in the collection and thirdly is the initial value of the reducer. As an example, in vanilla JavaScript we could do the following:

constnumbersToAdd=[1,2,3];functionadditionReducer(previous,current){returnprevious+current;}constresult=numbersToAdd.reduce(additionReducer,0);console.log(result);// 6
Enter fullscreen modeExit fullscreen mode

Wereduce our collection passing in a reducer function which receives aprevious andcurrent value and adds the two together and finally we have the initial value of0. What this will do is run the reducer for each iteration of the collection and use the initial value as the initial value ofprevious and when we return the result of addingprevious andcurrent, that value will then become the value ofprevious on the next iteration until there are no more items in the collection to iterate and thus, the result is returned.

Tests

describe('reduce',()=>{it('should apply the addition reducer correctly',()=>{constcollection=[1,2,3];constreducerFn=(previous,current)=>previous+current;constactual=reduce(collection,reducerFn,0);constresult=6;expect(actual).toStrictEqual(result);});it('should return a new array of multiplied values correctly',()=>{constcollection=[1,2,3];constreducerFn=(previous,current)=>{previous.push(current*2);returnprevious;};constactual=reduce(collection,reducerFn,[]);constresult=[2,4,6];expect(actual).toStrictEqual(result);});it('should reduce a collection of objects and reshape them via the reducer',()=>{constpokemon=[{name:"charmander",type:"fire"},{name:"squirtle",type:"water"},{name:"bulbasaur",type:"grass"}];functionpokemonReducer(output,current){output[current.name]={type:current.type};returnoutput;}constactual=reduce(pokemon,pokemonReducer,{});constresult={charmander:{type:'fire'},squirtle:{type:'water'},bulbasaur:{type:'grass'}};expect(actual).toStrictEqual(result);});});
Enter fullscreen modeExit fullscreen mode

Here we can see 3reduce tests which work on similar data but produce values of differing types. That is to say that we have a simple addition reducer just as with the example given in the introduction of this article but also a more complex multiplication reducer which basicallyacts like amap function would since it generates a new array of multiplied values. Lastly we see a far more complex reducer which takes a collection of objects and returns a new state representation of each object as a new collection.

Implementation

The native JavaScript implementation ofreduce has the following signature:

arr.reduce(functioncallback(accumulator,currentValue[,index[,array]]){// perform actions and return the next state}[,initialValue]);
Enter fullscreen modeExit fullscreen mode

We will aim to reproduce this behaviour with the following implementation:

/** * @function reduce * @description A function to a collections values into any other type * @param {Array} collection - The collection to reduce * @param {Function} reducerFn - The reducer function to be applied on the last and current value * @param {*} initialValue - The initial value to apply the reducer to * @returns {*} The reduced value, this will be the same type as the initialValue parameter */functionreduce(collection,reducerFn,initialValue){letoutput=initialValue;constclone=[...collection];for(letindex=0;index<clone.length;index++){output=reducerFn(output,clone[index],index,clone);}returnoutput;}
Enter fullscreen modeExit fullscreen mode

TheinitialValue will be the defaultoutput of thereduce function if no items in the collection exist. If items exist in the collection then for each one we will reassignoutput to the value of thereducerFn function. ThereducerFn function takes the same parameters as the native JavaScript implementation since that is our goal to reproduce. These parameters are theaccumulator,currentValue,index,array in the native implementation but in our case they areoutput,clone[index],index andclone.

Note: If you haven't read the previous articles on Array method algorithms in this series, we are cloning the array to avoid mutations on the initial collection that is provided so that that remains intact.

Finally, once ourreducerFn function commits actions against each element and generates a finaloutput value, we exit the loop and return theoutput value.

Using our example of the native implementation near the top of this article, we could do the following to achieve the same results:

constnumbersToAdd=[1,2,3];functionreduce(collection,reducerFn,initialValue){letoutput=initialValue;constclone=[...collection];for(letindex=0;index<clone.length;index++){output=reducerFn(output,clone[index],index,clone);}returnoutput;}functionadditionReducer(previous,current){returnprevious+current;}constresult=reduce(numbersToAdd,additionReducer,0);console.log(result);// 6
Enter fullscreen modeExit fullscreen mode

Conclusions

Reducers can be quite a complex topic to discuss but just remember, a reducer merely reduces a collection to a single value. That value could be anything you want it to be but that is all it does. I love using reducers in my day to day work as they can make complex tasks much easier andlibraries such as Redux use reducers as a core part of their functionality to do some real heavy lifting. Reducers are also useful for mundane tasks though such as ouradditionReducer example and so you can adapt them to many use cases quite easily. In saying this though you do want to scope reducers to highly specific use cases and they should adhere strictly to the Single Responsibility Principle as with any function or method implementation.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I like to build cool things, work with nice people and help others where I can. Currently I'm an engineering manager for a fintech startup and historically a serial founder & freelancer software dev.
  • Location
    München, Deutschland 🇩🇪
  • Education
    The Open University
  • Work
    Engineering Manager @ Deutsche Fintech Solutions GmbH
  • Joined

More fromJames Robb

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp