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}
];
2

8 Answers 8

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

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

1 Comment

I could not imagine that this could be done even more inefficiently than what the OP tried to do... :)
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 });
  }
}

Comments

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

Comments

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

Comments

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

Comments

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

Comments

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

Comments

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

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.