如何使用JavaScript从数组中的嵌套值计算平均值?

时间:2019-07-09 17:53:31

标签: javascript

我正在尝试计算每个阶段的平均持续时间。因此,在下面的数组中-我应该能够获得“ test1”的平均持续时间,即为2。

jobs = [
{
    "build_id": 1,
    "stage_executions": [
        {
          "name": "test1"
          "duration": 1,

        },

        {
          "name": "test2"
          "duration": 16408,

        },

        {
          "name": "test3"
          "duration": 16408,

        },
    ]
 },
 {
    "build_id": 2,
    "stage_executions": [
        {
          "name": "test1"
          "duration": 3,

        },

        {
          "name": "test2"
          "duration": 11408,

        },

        {
          "name": "test3"
          "duration": 2408,

        },
    ]
 }
]

我的尝试失败:

avgDuration: function(jobs) {
  let durationSum = 0
  for (let item = 0; item < this.jobs.length; item++) {
    for (let i = 0; i < this.jobs[item].stage.length; item++) {
      durationSum += stage.duration
    }
    durationAverage = durationSum/this.jobs[item].stage.length
  }
  return durationAverage

我在做什么错?我不确定如何完成此任务,因为工期是在每个工作之间分配的。

更新: 这是所有阶段的平均回报,而不是每个阶段

<template>
  <div class="stages">
    <h3>
      Average Duration
    </h3>
    <table>
      <tbody>
          <tr v-for="item in durations">
              <td>
              <b>{{ item.average}} {{ item.count }}</b>
              // this returns only 1 average and 177 count instead of 10 
              <br />
            </td>
          </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import { calculateDuration } from "../../helpers/time.js";
import { liveDuration } from "../../helpers/time.js";
import moment from "moment";

export default {
  name: "Stages",
  data() {
    return {
      jobs: [],
      durations: []
    };
  },
  methods: {
    avgDuration: function(jobs) {
      var averageByName = {}; // looks like { 'name': { average: 111, count: 0 }}
      for (var job of jobs) {
        for(var stage of job.stage_execution) {
              if (averageByName[stage.name] == null) { // we need a new object
                averageByName[stage.name] = { average: 0, count: 0 };
              }
              // just name it so its easier to read
              var averageObj = averageByName[stage.name];
              // update count
              averageObj.count += 1;
              // Cumulative moving average
              averageObj.average = averageObj.average + ( (stage.duration - averageObj.average) / averageObj.count );
              console.log(averageObj.count)
            }
          }
      return averageByName
    },
  },
  created() {
    this.JobExecEndpoint =
      process.env.VUE_APP_TEST_URL +
      "/api/v2/jobs/?limit=10";
    fetch(this.JobExecEndpoint)
      .then(response => response.json())
      .then(body => {
        for (let i = 0; i < body.length; i++) {
        this.jobs.push({
          name: body[i].job.name,
          job: body[i].job,
          stage_execution: body[i].stage_executions,
        });
      }
      })
      .then(() => {
        this.$emit("loading", true);
      })
      .then(() => {
        this.durations = this.avgDuration(this.jobs);
      })
      .catch(err => {
        console.log("Error Fetching:", this.JobExecEndpoint, err);
        return { failure: this.JobExecEndpoint, reason: err };
      });
  }
};
</script>

3 个答案:

答案 0 :(得分:1)

您可以为每个索引收集对象中的值,然后仅映射平均值。

var jobs = [{ build_id: 1, stage_executions: [{ name: "test1", duration: 1 }, { name: "test2", duration: 16408 }, { name: "test3", duration: 16408 }] }, { build_id: 2, stage_executions: [{ name: "test1", duration: 3 }, { name: "test2", duration: 11408 }, { name: "test3", duration: 2408 }] }],
    averages = jobs
       .reduce((r, { stage_executions }) => {
           stage_executions.forEach(({ duration }, i) => {
               r[i] = r[i] || { sum: 0, count: 0 };
               r[i].sum += duration;
               r[i].avg = r[i].sum / ++r[i].count;
           });
           return r;
       }, []);

console.log(averages.map(({ avg }) => avg));
console.log(averages);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:1)

使用累积移动平均值和几个循环,我们可以非常简单地进行此操作,并且不会有太多的数字导致溢出。

以下是与Wikipedia page on Moving Averages相关的行以及以下最相关的公式。

Moving Average Formula

由于上面有很多描述这种事情的文档,因此我不会在上面详细介绍。但是,我要说的是,将所有值加在一起的主要原因是溢出的可能性要低得多,这就是为什么我在本示例中使用它。

这是我用代码注释的解决方案。

var jobs = [ { "build_id": 1, "stage_executions": [ { "name": "test1", "duration": 1, }, { "name": "test2", "duration": 16408, }, { "name": "test3", "duration": 16408, }, ] }, { "build_id": 2, "stage_executions": [ { "name": "test1", "duration": 3, }, { "name": "test2", "duration": 11408, }, { "name": "test3", "duration": 2408, }, ] } ];

var averageByName = {}; // looks like { 'name': { average: 111, count: 0 }}
for (var job of jobs) {
    for(var stage of job.stage_executions) {
        if (averageByName[stage.name] == null) { // we need a new object
            averageByName[stage.name] = { average: 0, count: 0 };
        }
        // just name it so its easier to read
        var averageObj = averageByName[stage.name];
        // update count
        averageObj.count += 1;
        // Cumulative moving average
        averageObj.average = averageObj.average + ( (stage.duration - averageObj.average) / averageObj.count );
    }
}
// print the averages
for(var name in averageByName) {
    console.log(name, averageByName[name].average);
}

让我知道您是否有任何疑问或不清楚的地方。

答案 2 :(得分:0)

我使用Array.prototype.flatMap将jobs数组展平为{name:string,duration:number}对象的数组。此外,为了使更多的解决方案更具动态性,该函数接受一个field参数,该参数返回该特定字段的平均值。

 
   const jobs = [
  {
    "build_id": 1,
    "stage_executions": [
      {
        "name": "test1",
        "duration": 1,

      },

      {
        "name": "test2",
        "duration": 16408,

      },

      {
        "name": "test3",
        "duration": 16408,

      },
    ]
  },
  {
    "build_id": 2,
    "stage_executions": [
      {
        "name": "test1",
        "duration": 3,

      },

      {
        "name": "test2",
        "duration": 11408,

      },

      {
        "name": "test3",
        "duration": 2408,

      },
    ]
  }
];

const caller = function(jobs, field) {
  const filtered = jobs
    .flatMap((item) => item.stage_executions)
    .filter(item => {
      return item.name === field;
    })
  const total = filtered.reduce((prev, curr) => {
    return prev + curr.duration;
  }, 0)
  return total / filtered.length;
}

console.log(caller(jobs, 'test1'))
console.log(caller(jobs, 'test2'))
console.log(caller(jobs, 'test3'))

如果出现错误flatMap is not a function。您可以在polyfill或js文件顶部添加此代码段。

Array.prototype.flatMap = function(lambda) {
  return Array.prototype.concat.apply([], this.map(lambda));
};

PS:为了演示,我从here获得了flatMap的实现