简体   繁体   中英

MongoDB: How to count average of a nested array

I would like to count the average of "grades" from distinct " course " ("IABD", NPAD"...) via a dedicated function taking ID and NAME (of the course) as parameters in the collection documents with a structure like below.

{
        "_id" : ObjectId("58e8ba731c9f5cc5bf605e27"),
        "ID" : 123457,
        "name" : "Stack",
        "surname" : "Overflow",
        "grades" : [
                {
                        "IABD" : [
                                2,
                                3,
                                4,
                                5
                        ]
                },
                {
                        "NPAD*" : [
                                3.5,
                                2.5
                        ]
                },
                {
                        "SIDB" : [
                                5,
                                3.5
                        ]
                },
                {
                        "NPAD" : [
                                5,
                                2
                        ]
                },
                {
                        "IABD" : [
                                4,
                                6
                        ]
                }
        ]
}

The following one doesnt work:

db.studenci.aggregate([ { $project: { courseAvg: { $avg: '$grades.NPAD'} } } ])

Tried to start with getting the array with grades via:

function avg(ID,course) { doc = db.collection.find({ID: ID}); doc1 = doc[0]; return doc1.grades[0]; }

But could not dig into the array values to count the average...

Once I get to the values wanted to make a simple AVG hardcoding...

                var avg = 0 ;
                var summ = 0;
                for(var i = 0 ; i < numbers.length ; i++){
                    summ = summ + numbers[i]};
                    return summ
                    }   
                avg = summ / numbers.length;
                return avg;

I'm looking forward to your reply.

This is not an elegant solution (i'm not an expert about MongoDB) but this works (i tested locally on my mongo):

function avgByIdAndCourse(id, course){

//THIS VARIABLE WILL STORE THE RESULT OF AVG
var res;

//REPLACE "N" WITH YOU COLLECTION NAME
db.n.find(ObjectId(id)).forEach( function(doc) {

//CREATE THE GRADES ARRAY
var arr = doc.grades;

//VARIABLE FOR AVG CALCULATION
var count = 0;

for(var i = 0; i < arr.length; i++){
    var obj = arr[i];

    //LOOP OVER GRADES KEY
    for(var key in obj){

    //IF THE COURSE MATCHES WITH OUR COURSE PARAMETER
    if(key == course){
        count = 0;

    //ARRAY OF VALUES OF COURSE
    var a = obj[key];
    for(var j = 0; j < a.length; j++){
        count += a[j];
    }
    // just a print for make sure it works ---> print(count/a.length);

    //STORE THE RESULT IN RES VARIABLE
    res = count/a.length;
    }

}
  }

  //REQUIRED BY FOREACH CALLBACK(NOT SURE ABOUT THAT)
  db.n.save(doc);
 });

//RETURN THE RESULT
return res;
}

You can try to optimize the loop over the key part (thinking about that we don't need to loop over the key, we can simply check if that key exists by trying access to it).

If you copy and paste this on your shell you can test it in this way:

var result = avgByIdAndCourse('PUTIDTOFINDHERE', 'COURSENAME');
print(result);

Now you should see the avg of that course.

Hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM