28

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?

4
  • What’s a.count = + 1; supposed to achieve? Commented Jun 6, 2017 at 10:36
  • Is the format of a fixed? Commented Jun 6, 2017 at 10:36
  • I guess you mean += Commented Jun 6, 2017 at 10:37
  • no .. a is just a empty array in the beginning. b is fixed. Commented Jun 6, 2017 at 10:39

4 Answers 4

63

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.

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

8 Comments

does this change the original array? I want the result in a different array to the orginal..
This is EXACTLY what reduce is supposed to be used for. No, it doesn't change the original array.
I tried your code .. (i have updated my code) but it says counts.map is not a function. Note: I did change a few things.. like remove the object keys function and changed the arrow function .. could u pls check it..
Your problem is having removed Object.keys. The counts are stored in an object, while map is defined for arrays. We take the keys of the object (all names that were encountered) in an array and map that to the format you want. There is no such function directly for objects.
@Y.Hewa I have rolled back your edit to keep the question clean, hope you don't mind. The issue should be resolved when you add Object.keys back. Just out of curiosity, why did you remove it?
|
3

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

2 Comments

Actually you could do: b.issues.filter(({fields:{status:{name}}}) => name === "Backlog").length
This method wont work when we don't know the property name or there are too many property names.
1

You can write like this. It is dynamic.

var a = {}; 
for(var key in b["issues"]){ 
    if(!a.hasOwnProperty(b["issues"][key].fields.status.name)){
     a[b["issues"][key].fields.status.name] = 1;
    }else{
     a[b["issues"][key].fields.status.name] = a[b["issues"][key].fields.status.name]+1;
    }
}
var c = [];
for(var key1 in a){
   c.push({
   name  : key1,
   count : a[key1]
   });
}

Comments

0

Something like this should do the trick. Simply iterate over your data, keep 2 counters with the number of each type of issue, and create the data format you want in the end. Try it live on jsfiddle.

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

var data = [];
for(var issue of b.issues){
    var entryFound = false;
    var tempObj = {
        name: issue.fields.status.name,
        count: 1
    };

    for(var item of data){
        if(item.name === tempObj.name){
        item.count++;
        entryFound = true;
        break;
      }
    }

    if(!entryFound){
        data.push(tempObj);
    }
}
console.log(data);

2 Comments

The thing is if there are around 10 statuses i would have to write if conditions for all of them.. is there a way to get the current status and check with the array like how ive tried to do .. ?
@Y.Hewa Check my updated answer. This should cover your case. You can iterate over your input and if you find it already in your list simply increase its number count by one, if not simply push it as a new item to your data array.

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.