3

I have following array of hashes and want to convert it into an array like the one at the bottom of the post.

var responseData = [
  {deviceType: "Smartphone", deviceCount: 14},
  {deviceType: "Tablet", deviceCount: 11},
  {deviceType: "Notebook", deviceCount: 3},
  {deviceType: "Desktop", deviceCount: 2},
  {deviceType: "Smartphone", deviceCount: 1},
  {deviceType: "Tablet", deviceCount: 10},
  {deviceType: "Notebook", deviceCount: 30},
  {deviceType: "Desktop", deviceCount: 20}
];

function dataMapper(responseData){
  let series = [];
  if(responseData && responseData.length){
  responseData.forEach(function(resource){
  existingElement = series.filter(function (item) {
      return item.deviceType === resource.deviceType;
    });
  if (existingElement) {
    deviceCount = existingElement[0].deviceCount + resource.deviceCount;
    existingElement[0].deviceCount = deviceCount
  }else{
    series[0].push({deviceType: resource.deviceType, y: resource.deviceCount});
  }
    });
  }
  return series
}

console.log(dataMapper(responseData))

I want to convert this into:

var expectedResult = [
    {deviceType: "Smartphone", deviceCount: 15},
    {deviceType: "Tablet", deviceCount: 21},
    {deviceType: "Notebook", deviceCount: 33},
    {deviceType: "Desktop", deviceCount: 22}
];
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Gol. D Roger
  • 119
  • 1
  • 7
  • your filter method is not working correctly – messerbill Feb 19 '18 at 13:12
  • Possible duplicate of [Sum javascript object propertyA values with same object propertyB in array of objects](https://stackoverflow.com/questions/19233283/sum-javascript-object-propertya-values-with-same-object-propertyb-in-array-of-ob) – Heretic Monkey Feb 19 '18 at 13:55

8 Answers8

5

Using an ES6 Map and reduce:

const responseData = [{deviceType: "Smartphone", deviceCount: 14},{deviceType: "Tablet", deviceCount: 11},{deviceType: "Notebook", deviceCount: 3},{deviceType: "Desktop", deviceCount: 2},{deviceType: "Smartphone", deviceCount: 1},{deviceType: "Tablet", deviceCount: 10},{deviceType: "Notebook", deviceCount: 30},{deviceType: "Desktop", deviceCount: 20}];

const result = Array.from(
    responseData.reduce( 
        (acc, o) => (acc.get(o.deviceType).deviceCount += o.deviceCount, acc),
        new Map(responseData.map( ({deviceType}) => [deviceType, {deviceType, deviceCount: 0} ] )) 
    ).values()
);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
trincot
  • 317,000
  • 35
  • 244
  • 286
2

Its much easier if you use a hashtable and build up the result in parallel:

 const hash = {}, result = [];

for(const {deviceType, deviceCount} of responseData){
  if(hash[deviceType]){
     hash[deviceType].deviceCount += deviceCount;
  } else {
     result.push(hash[deviceType] = {deviceType, deviceCount});
  }
}

If you really want to iterate over the array, you should use find instead of filter:

const result = [];

for(const {deviceType, deviceCount} of responseData){
  const exists = result.find(device => device.deviceType === deviceType);
 if(exists){
   exists.deviceCount += deviceCount;
  } else {
   result.push({deviceType, deviceCount });
  }
}
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
2

You could take a hash table as reference to the counting object and return the array with the objects.

var responseData = [{ deviceType: "Smartphone", deviceCount: 14 }, { deviceType: "Tablet", deviceCount: 11 }, { deviceType: "Notebook", deviceCount: 3 }, { deviceType: "Desktop", deviceCount: 2 }, { deviceType: "Smartphone", deviceCount: 1 }, { deviceType: "Tablet", deviceCount: 10 }, { deviceType: "Notebook", deviceCount: 30 }, { deviceType: "Desktop", deviceCount: 20 }],
    hash = Object.create(null),
    result = responseData.reduce(function (r, o) {
        if (!hash[o.deviceType]) {
            hash[o.deviceType] = { deviceType: o.deviceType, deviceCount: 0 };
            r.push(hash[o.deviceType]);
        }
        hash[o.deviceType].deviceCount += o.deviceCount;
        return r;
    }, []);

console.log(result);

Another solution could be to use a Map and get later an array of all counted devices.

var responseData = [{ deviceType: "Smartphone", deviceCount: 14 }, { deviceType: "Tablet", deviceCount: 11 }, { deviceType: "Notebook", deviceCount: 3 }, { deviceType: "Desktop", deviceCount: 2 }, { deviceType: "Smartphone", deviceCount: 1 }, { deviceType: "Tablet", deviceCount: 10 }, { deviceType: "Notebook", deviceCount: 30 }, { deviceType: "Desktop", deviceCount: 20 }],
    result = Array.from(
        responseData.reduce((map, { deviceType, deviceCount }) => map.set(deviceType, (map.get(deviceType) || 0) + deviceCount), new Map),
        ([ deviceType, deviceCount ]) => ({ deviceType, deviceCount })
    );

console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

One of the way where we bring your expected result is by doing like this:

var responseData = [{
        deviceType: "Smartphone",
        deviceCount: 14
      },
      {
        deviceType: "Tablet",
        deviceCount: 11
      },
      {
        deviceType: "Notebook",
        deviceCount: 3
      },
      {
        deviceType: "Desktop",
        deviceCount: 2
      },
      {
        deviceType: "Smartphone",
        deviceCount: 1
      },
      {
        deviceType: "Tablet",
        deviceCount: 10
      },
      {
        deviceType: "Notebook",
        deviceCount: 30
      },
      {
        deviceType: "Desktop",
        deviceCount: 20
      }
    ];
    var pushedType = [];
    var summedData = [];

    for (var i in responseData) {
      if (pushedType.indexOf(responseData[i].deviceType) === -1) {
        summedData.push(responseData[i]);
        pushedType.push(responseData[i].deviceType);
      } else {
        summedData[pushedType.indexOf(responseData[i].deviceType)].deviceCount += responseData[i].deviceCount;
        }
      }

      $(".result").html(JSON.stringify(summedData))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="result"></div>

Hope it helps you :)

Arun AK
  • 4,353
  • 2
  • 23
  • 46
0

I suggest to use lodash

let result = _.groupBy(responseData , 'deviceType');
result = Object.keys(result).map(key => {
   return  { 
       deviceType: key,
       deviceCount: _.sumBy(result[key], 'deviceCount')} ;
});

or with Array.reduce():

let transformObj = {};
   responseData.reduce((prev,cur) =>{
      const key = cur.deviceType;
      if(!prev[key]){
         prev[key] =  0;
      }
      prev[key] += cur.deviceCount;

      return prev;
   },transformObj);

  const results = Object.keys(transformObj).map(key => {
      return {
           deviceType: key,
           deviceCount: transformObj[key]
      }
  });
Kai
  • 3,104
  • 2
  • 19
  • 30
0

I added this fiddle for you: https://jsfiddle.net/6zuux7jq/6/

var responseData = [
  {deviceType: "Smartphone", deviceCount: 14},
  {deviceType: "Tablet", deviceCount: 11},
  {deviceType: "Notebook", deviceCount: 3},
  {deviceType: "Desktop", deviceCount: 2},
  {deviceType: "Smartphone", deviceCount: 1},
  {deviceType: "Tablet", deviceCount: 10},
  {deviceType: "Notebook", deviceCount: 30},
  {deviceType: "Desktop", deviceCount: 20}
];
 
function dataMapper(responseData){
  var series = {};
  if(responseData && responseData.length){
  responseData.forEach(function(element){
       series[element.deviceType] = Number.isInteger(series[element.deviceType]) ? series[element.deviceType] + element.deviceCount : element.deviceCount
    });
  }
  var tmp = []
  for (var i in series) {
    tmp.push({deviceType: i, deviceCount: series[i]})
  }
  return tmp
}

console.log(dataMapper(responseData))

Greetings

messerbill
  • 5,499
  • 1
  • 27
  • 38
0

Try this

const results = []
var temp = {};

responseData.map(function(item){
    temp[item.deviceType] = (temp[item.deviceType] || 0) + item.deviceCount;;
})

Object.keys(temp).forEach(function(key) {
    results.push({ deviceType: key, deviceCount: temp[key]});
})

console.log(results);
fyasir
  • 2,924
  • 2
  • 23
  • 36
0

Here is my solution:

responseData.forEach(function(item) {
if (expectedResult[item.deviceType] == null) {
   expectedResult[item.deviceType] = item;
} else {
expectedResult[item.deviceType].deviceCount += item.deviceCount;
}
});
The KNVB
  • 3,588
  • 3
  • 29
  • 54