Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group and count values in an array

I have an array with objects, like the following.

b = {
  "issues": [{
    "fields": {
      "status": {
        "id": "200",
        "name": "Backlog"
      }
    }
  }, {
    "fields": {
      "status": {
        "id": "202",
        "name": "close"
      }
    }
  }, {
    "fields": {
      "status": {
        "id": "201",
        "name": "close"
      }
    }
  }]
};

I want to count how many issues have status close, and how many have backlog. I'd like to save the count in a new array as follows.

a = [
  {Name: 'Backlog', count: 1},
  {Name: 'close', count: 2}
];

I have tried the following.

b.issues.forEach(function(i) {
  var statusName = i.fields.status.name;

  if (statusName in a.Name) {
    a.count = +1;
  } else {
    a.push({
      Name: statusName,
      count: 1
    });
  }
});

That however doesn't seem to be working. How should I implement this?

like image 495
Shayuh Avatar asked Jun 06 '17 10:06

Shayuh


Video Answer


2 Answers

This is a perfect opportunity to use Array#reduce. That function will take a function that is applied to all elements of the array in order and can be used to accumulate a value. We can use it to accumulate an object with the various counts in it.

To make things easy, we track the counts in an object as simply {name: count, otherName: otherCount}. For every element, we check if we already have an entry for name. If not, create one with count 0. Otherwise, increment the count. After the reduce, we can map the array of keys, stored as keys of the object, to be in the format described in the question. See below.

var b = {
  "issues": [{
    "fields": {
      "status": {
        "id": "200",
        "name": "Backlog"
      }
    }
  }, {
    "fields": {
      "status": {
        "id": "202",
        "name": "close"
      }
    }
  }, {
    "fields": {
      "status": {
        "id": "201",
        "name": "close"
      }
    }
  }]
};

var counts = b.issues.reduce((p, c) => {
  var name = c.fields.status.name;
  if (!p.hasOwnProperty(name)) {
    p[name] = 0;
  }
  p[name]++;
  return p;
}, {});

console.log(counts);

var countsExtended = Object.keys(counts).map(k => {
  return {name: k, count: counts[k]}; });

console.log(countsExtended);
.as-console-wrapper {
  max-height: 100% !important;
}

Notes.

  1. Array#reduce does not modify the original array.
  2. You can easily modify the function passed to reduce to for example not distinguish between Backlog and backlog by changing

    var name = c.fields.status.name;
    

    into

    var name = c.fields.status.name.toLowerCase();
    

    for example. More advanced functionality can also easily be implemented.

like image 162
Just a student Avatar answered Oct 06 '22 07:10

Just a student


Using ES6 Arrow functions you can do it with minimum syntax

var b = {
    "issues": [{
        "fields": {
            "status": {
                "id": "200",
                "name": "Backlog"
            }
        }
    }, {
        "fields": {
            "status": {
                "id": "202",
                "name": "close"
            }
        }
    }, {
        "fields": {
            "status": {
                "id": "201",
                "name": "close"
            }
        }
    }]
};

var countOfBackLog = b.issues.filter(x => {
return x.fields.status.name === "Backlog"
}).length

var countOfClose = b.issues.filter(x => {
return x.fields.status.name === "close"
}).length

a =[{Name: 'Backlog', count : countOfBackLog}, {Name: 'close', count : countOfClose}]

More about arrow functions here

like image 32
2 revs, 2 users 72% Avatar answered Oct 06 '22 09:10

2 revs, 2 users 72%