François Richard
François Richard

Reputation: 7045

javascript several corresponding array reducing/sumup

What is the cleanest way to reduce those array ?

data = {
    id: [1, 1, 1, 3, 3, 4, 5, 5, 5, ...]
    v: [10,10,10, 5, 10 ...]
}

For each id there is a v corresponding. What I want is sum up v for each id. In this example the result should be

data = {
    id: [1, 3, 4, 5, ...]
    v: [30, 15, ...]
}

Upvotes: 6

Views: 107

Answers (7)

vorillaz
vorillaz

Reputation: 6286

I would go for the Array.prototype.reduce() ,simple and elegant solution

var ids = [1, 1, 1, 3, 3, 3, 3, 4, 5, 6, 6, 6],
  v = [10, 10, 10, 5, 10, 10, 10, 404, 505, 600, 60, 6],
  data = {};
data.v = [];
data.ids = ids.reduce(function(a, b, index) {
  if (a.indexOf(b) < 0) a.push(b);
  if (!data.v[a.indexOf(b)]) data.v[a.indexOf(b)] = 0;
  data.v[a.indexOf(b)] += v[index];
  return a;
}, []);

https://jsfiddle.net/2ssbngLr/

Upvotes: 3

Nina Scholz
Nina Scholz

Reputation: 386660

This is a solution for ordered id with Array.prototype.reduce().

var data = {
        id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
        v: [10, 10, 10, 5, 10, 7, 8, 10, 13]
    },
    result = { id: [], v: [] };

data.id.reduce(function (r, a, i) {
    if (r === a) {
        result.v[result.v.length - 1] += data.v[i];
    } else {
        result.id.push(a);
        result.v.push(data.v[i]);
    }
    return a;
}, -1);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Or a in situ version

var data = {
    id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
    v: [10, 10, 10, 5, 10, 7, 8, 10, 13]
};

void function (d) {
    var i = 1;
    while (i < d.id.length) {
        if (d.id[i - 1] === d.id[i]) {
            d.id.splice(i, 1);
            d.v[i - 1] += d.v.splice(i, 1)[0];
            continue;
        }
        i++;
    }
}(data);

document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');

Upvotes: 1

Nick Zuber
Nick Zuber

Reputation: 5637

Some people have posted some good solutions so far, but I haven't really seen one that does exactly what you're looking for. Here is one that takes your specific object and returns an object of the same format, but meeting your requirements and reduced.

// Your data object
data = {
    id: [1, 1, 1, 3, 3],
    v: [10,10,10, 5, 10]
}

// Assuming obj consists of `id` and `v`
function reduce(obj){
  // We create our reduced object 
  var reducedObj = {
    id: [],
    v: []
  }

  // Next we create a hash map to store keys and values
  var map = {};
  for(var i=0; i<obj.id.length; ++i){
    // If this key doesn't exist, create it and give it a value
    if(typeof map[parseInt(obj.id[i])] === 'undefined'){
      map[parseInt(obj.id[i])] = 0;
    }
    // Sum all of the values together for each key
    map[parseInt(obj.id[i])] += parseInt(obj.v[i]);
  }

  // Now we map back our hashmap to our reduced object
  for(var ele in map){
    reducedObj.id.push(ele);
    reducedObj.v.push(map[ele]);
  }

  // Return our new reduced object
  return reducedObj;
}

var myReducedObject = reduce(data);
console.log(myReducedObject);

Working Fiddle

Upvotes: 1

epascarello
epascarello

Reputation: 207527

If the ids are always in order, a simple for loop can solve it. There is no need to get overly complicated.

data = {
  id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
  v: [10, 10, 10, 5, 10, 1, 2, 3, 4]
};

var result = {
  id: [],
  v: []
};

(function() {
  var ids = data.id,
    vals = data.v,
    lastId = ids[0],
    runningTotal = vals[0];
  for (var i = 1; i < ids.length; i++) {

    if (lastId === ids[i]) {
        runningTotal += vals[i];
    }

    if (lastId !== ids[i] || i + 1 === ids.length) {
      result.id.push(lastId);
      result.v.push(runningTotal);
      lastId = ids[i];
      runningTotal = vals[i];
    }
  }

}());

console.log(result);

Upvotes: 1

Jamie Dixon
Jamie Dixon

Reputation: 53991

One way of doing this, given two arrays of equal length would be to map/reduce them:

const ids = [1, 1, 1, 3, 3];
const vs = [10,10,10,5,10];

const reduced = ids
.map((val, i) => ({ id: val, value: vs[i] }))
.reduce((agg, next) => {
    agg[next.id] = (agg[next.id] || 0) + next.value;
    return agg;
}, {});

console.log(reduced);

// Object {1: 30, 3: 15}

Working example: https://jsfiddle.net/h1o5rker/1/

Upvotes: 3

anni saini
anni saini

Reputation: 413

var data={
  id: [1,1,1,10,123,4531],
  v:[123,123,53,223,11,11,11]
},
 _v = data.v, vinit;

document.write(data.v+'<br>');
 for(var i=0;i<_v.length;i++){
  vinit = _v[i];
  for(var j=i+1; j<=_v.length;j++){
    if(_v[j]===vinit){
     delete _v[j];
    }
  }
 };

document.write(data.v);

var data={
  id: [1,1,1,10,123,4531],
  v:[123,123,53,223,11,11,11,...]
},
 _v = data.v, vinit;
 for(var i=0;i<_v.length;i++){
  vinit = _v[i];
  for(var j=i+1; j<=_v.length;j++){
    if(_v[j]===vinit){
     delete _v[j];
    }
  }
 }

the above code is just for the v but you can simultaneously reduce the repeating elements for id too by introducing some more variables

in the snippet you can see that there are the extra commas in the second line which shows that those elements were deleted

Upvotes: 1

Bek
Bek

Reputation: 3207

I think it can be accomplished with reduce

 var data = {
   id: [1, 1, 1, 3, 3],
   v: [10, 10, 10, 5, 10]
 }

 var sumsObjs = data.v.reduce(function(sum, val, index) {
   var id = data.id[index];
   if (sum[id] !== undefined) {
     sum[id] = sum[id] + val;
   } else {
     sum[id] = val;
   }
   return sum;
 }, {});

 console.log(sumsObjs);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>

Upvotes: 2

Related Questions