turtle
turtle

Reputation: 8093

Keeping a counter inside a reduce function

I am trying to write a reduce function that assigns an incrementing integer to each new unique value in an array and builds a dictionary mapping the unique element to the incrementing index.

My desired function consumes an array like this:

[3,1,1,2,3,1,5,1,2]

and outputs a map that looks like this:

The function looks like this:

{3: 0, 1: 1, 2: 2, 5: 3}

One potential solution is to use reduce, but it requires keeping the counter outside of the function:

var i = 0
var func = [3,1,1,2,3,1,5,1,2].reduce(function(a, b) {
  if (!(a in b) { 
    b[a] = i
    i++
  }
  return b
},{})

Is there a way to write this function but somehow keep the counter inside the function's scope? Obviously I could wrap everything in a function, but is there a way to do this with just reduce?

Upvotes: 0

Views: 1349

Answers (4)

pwnyexpress
pwnyexpress

Reputation: 1016

Array.prototype.reduce takes 4 arguments. You might consider using them, since one of them is the index value. And thus the need for a counter is not necessary.

var func = [3,1,1,2,3,1,5,1,2].reduce(function(prev, curr, idx, arr) {
    if(!(prev in curr)) curr[prev] = (prev.length > 0) ? prev.length - 1 : 0;
    return curr;
});

Upvotes: 2

Chuck
Chuck

Reputation: 237100

The answer is yes, but it's probably more awkward than just using a scoping function. Basically, you just make your state value a composite of the actual result you want and the additional state, and then you extract the result when you're done reducing.

var func = [3,1,1,2,3,1,5,1,2].reduce(function(memo, val) {
  if (!(val in memo[0])) { 
    memo[0][val] = memo[1]
    memo[1]++
  }
  return memo
}, [{}, 0])[0]

Upvotes: 0

Matt
Matt

Reputation: 20796

You can always throw i into the object itself:

[3,1,1,2,3,1,5,1,2].reduce(function(a,b) {
   if(a[b]===undefined) a[b] = a.i++; return a},{i:0})

// {1: 1, 2: 2, 3: 0, 5: 3, i: 4}

Upvotes: 0

Thomas Junk
Thomas Junk

Reputation: 5676

Using a closure will help keeping your code clean:

fun=function(){
 var i=0;
 return function(b, a) {
  if (!(a in b)) { 
    b[a] = i
    i++
  }
  return b;
};
}();

var func = [3,1,1,2,3,1,5,1,2].reduce(fun,{})

So you have fun while reducing.

Upvotes: 4

Related Questions