6

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, ...]
}
4
  • What have you tried to do? Using a functional approach (using maybe lodash) you can do this using a combination of zip, maps, groupBy and reduce Commented Dec 28, 2015 at 14:04
  • 1
    Is the data already sorted? Are the id's in always in ascending order like in the example? If so, I would just use a for loop and check if the current id is the same as the previous loop Commented Dec 28, 2015 at 14:05
  • @Branden this is way I started to resolve this but still wondering if anyone has a better solution that I ignore Commented Dec 28, 2015 at 14:08
  • A simple for loop is probably the best solution. BUT the answer differs if the ids in the data are sorted to start or if the order can be different. Commented Dec 28, 2015 at 14:14

7 Answers 7

3

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/

Sign up to request clarification or add additional context in comments.

Comments

3

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/

Comments

2

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>

Comments

1

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

Comments

1

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);

2 Comments

I think that last value of v should be 9 not 5.
@jcubic Brain has not woken up from the long weekend. :)
1

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

Comments

1

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>');

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.