alexc
alexc

Reputation: 1310

calculate average result from multi-dimensionally sorted array (JavaScript)

Below is the layout of my JSON File.

{
    "questions": ["Question1", "Question2"],
    "orgs": ["Org1", "Org2", "Org3"],
    "dates": ["Q1", "Q2", "Q3"],
    "values": [
        [
            [5, 88, 18],
            [50, 83, 10],
            [29, 78, 80]

        ],
        [
            [46, 51, 61],
            [95, 21, 15],
            [49, 86, 43]
        ]
    ]
}

I'm trying to retrieve a single array of values by looping through each question, indexed by an "orgs" value and then adding each value retrieved and dividing it by data.dates.length.

Here is my code;

d3.json("data.json", function(error, data) {

  var array = new Array()
  var orgS = "Org2"
  var org = data.orgs.indexOf(orgS);

  for (var question = 0; question < data.questions.length; question++) {
    array.push(
      data.values[question][org]
    )

    console.log(array)
  }

  // add array together
  array.reduce(function(a, b) {
    return a + b;
  })

  // calculate average
  var avg = array / data.dates.length;
})

Here is a plnk;

http://plnkr.co/edit/wMv8GmkD1ynjo9WZVlMb?p=preview

I think the issue here is how I'm retrieving the values in the first place? as at the moment, although I am retrieving the correct values in the console log, I'm getting the array twice, and both times inside nested arrays. I'm not so sure how to remedy the problem?

For reference;

[question1][org1] corresponds to the values [5, 88, 18].

Hope someone can offer some advice here?

Thanks!

Upvotes: 1

Views: 61

Answers (3)

Nina Scholz
Nina Scholz

Reputation: 386522

You need to store the sum, the result of reduce.

// add array together
// store in sum
var sum = array.reduce(function(a, b) {
    return a + b;
}, 0); // use 0 as start value

For the average, you do not need the length of data.dates but from array, because you collecting the values and this length is important.

// calculate average
var avg = sum / array.length;

Together for all values, you might get this

var data = { "questions": ["Question1", "Question2"], "orgs": ["Org1", "Org2", "Org3"], "dates": ["Q1", "Q2", "Q3"], "values": [[[5, 88, 18], [50, 83, 10], [29, 78, 80]], [[46, 51, 61], [95, 21, 15], [49, 86, 43]]] },
    sum = [];

data.values.forEach(function (a, i) {
    sum[i] = sum[i] || [];
    a.forEach(function (b) {
        b.forEach(function (c, j) {
            sum[i][j] = sum[i][j] || 0;
            sum[i][j] += c;
        });
    });
});

data.avg = sum.map(function (a, i) {
    return a.map(function (b) {
        return b / data.values[i].length;
    });
});

console.log(sum);
console.log(data);

Upvotes: 0

forgivenson
forgivenson

Reputation: 4435

Since you clarified your question to indicate you want to calculate separate averages for each question, I've rewritten my answer. You should do all the calculations in the for loop, since the loop is looping through the questions. Then store your averages in an array.

d3.json("data.json", function(error, data) {

  var averages = new Array()
  var orgS = "Org2"
  var org = data.orgs.indexOf(orgS);

  var values, sum;

  for (var question = 0; question < data.questions.length; question++) {
    // get the values for the question/org
    values = data.values[question][org];

    // calculate the sum
    sum = values.reduce(function(a, b) {
      return a + b;
    });

    // calculate the average
    averages.push(sum / values.length);
  }

  console.log(averages);

});

Upvotes: 1

user1106925
user1106925

Reputation:

Perform the .reduce() in the for loop and push that result into array. That will give you the an array of the results you expected.

array.push(data.values[question][org].reduce(function(a, b) {
  return a + b
}, 0) / data.dates.length)

[
  47.666666666666664,
  43.666666666666664
]

Currently, you're attempting to perform addition on the arrays themselves in the .reduce() callback instead of reducing the members of each individual array to their sum, and then average.

Demo: (Click the text below to show the whole function)

var data = {
  "questions": ["Question1", "Question2"],
  "orgs": ["Org1", "Org2", "Org3"],
  "dates": ["Q1", "Q2", "Q3"],
  "values": [
    [
      [5, 88, 18],
      [50, 83, 10],
      [29, 78, 80]

    ],
    [
      [46, 51, 61],
      [95, 21, 15],
      [49, 86, 43]
    ]
  ]
}

x(data)

// Your callback function.
function x(data) {
  var array = new Array()
  var orgS = "Org2"
  var org = data.orgs.indexOf(orgS);

  for (var question = 0; question < data.questions.length; question++) {
    array.push(data.values[question][org].reduce(function(a, b) {
      return a + b
    }, 0) / data.dates.length)
  }

  console.log(array)
}


Instead of a for loop, you could also use .map().

var array = data.questions.map(function(_, question) {
  return data.values[question][org].reduce(function(a, b) {
    return a + b
  }, 0) / data.dates.length
})

Demo: (Click the text below to show the whole function)

var data = {
  "questions": ["Question1", "Question2"],
  "orgs": ["Org1", "Org2", "Org3"],
  "dates": ["Q1", "Q2", "Q3"],
  "values": [
    [
      [5, 88, 18],
      [50, 83, 10],
      [29, 78, 80]

    ],
    [
      [46, 51, 61],
      [95, 21, 15],
      [49, 86, 43]
    ]
  ]
}

x(data)

// Your callback function.
function x(data) {
  var orgS = "Org2"
  var org = data.orgs.indexOf(orgS);

  var array = data.questions.map(function(_, question) {
    return data.values[question][org].reduce(function(a, b) {
      return a + b
    }, 0) / data.dates.length
  })

  console.log(array)
}

Upvotes: 1

Related Questions