Это исходные данные JSON:

var data= [
      { project: "ABC",    area: 'Test',       subArea: 'Dev',     done: 10 },
      { project: "ABC",    area: 'Test',       subArea: 'QA',      done: 10 },
      { project: "ABC",    area: 'Test',       subArea: 'Support', done: 10 },
      { project: "ABC",    area: 'External',   subArea: 'Dev',     done: 5  },
      { project: "ABC",    area: 'External',   subArea: 'QA',      done: 5  },
      { project: "ABC",    area: 'External',   subArea: 'Support', done: 5  },
      { project: "ABC",    area: 'Escalation', subArea: 'Dev',     done: 20 },
      { project: "ABC",    area: 'Escalation', subArea: 'QA',      done: 20 },
      { project: "ABC",    area: 'Escalation', subArea: 'Support', done: 20 },
      { project: "ABC123", area: 'Test',       subArea: 'Dev',     done: 20 },
      { project: "ABC123", area: 'Test',       subArea: 'QA',      done: 20 },
      { project: "ABC123", area: 'Test',       subArea: 'Support', done: 20 },
      { project: "ABC123", area: 'External',   subArea: 'Dev',     done: 10 },
      { project: "ABC123", area: 'External',   subArea: 'QA',      done: 10 },
      { project: "ABC123", area: 'External',   subArea: 'Support', done: 10 },
      { project: "ABC123", area: 'Escalation', subArea: 'Dev',     done: 5  },
      { project: "ABC123", area: 'Escalation', subArea: 'QA',      done: 5  },
      { project: "ABC123", area: 'Escalation', subArea: 'Support', done: 5  },
    ];

Мне нужно сгруппировать элементы по проекту , разделить их на области и суммировать выполненные часы для каждого подрайона . , Новый результат JSON должен быть таким:

    [
   {
      "name": "ABC",
      "test": {
         "total": 30,
         "totalDev": 10,
         "totalQA": 10,
         "totalSup": 10
      },
      "external": {
         "total": 15,
         "totalDev": 5,
         "totalQA": 5,
         "totalSup": 5
      },
      "escalation": {
         "total": 60,
         "totalDev": 20,
         "totalQA": 20,
         "totalSup": 20
      }
   },
   {
      "name": "ABC123",
      "test": {
         "total": 60,
         "totalDev": 20,
         "totalQA": 20,
         "totalSup": 20
      },
      "external": {
         "total": 30,
         "totalDev": 10,
         "totalQA": 10,
         "totalSup": 10
      },
      "escalation": {
         "total": 15,
         "totalDev": 5,
         "totalQA": 5,
         "totalSup": 5
      }
   }
]

С моим реальным кодом ниже я уже получаю желаемый результат, но я думаю, что код может быть более оптимизирован. Без повторения и всех условных проверок, без жесткого кода свойства области и подрайонов внутри блока карты, потому что эти области значений динамические на стороне сервера.

var sum = (total, item) => total += item.done;

result = _.chain(data)
          .groupBy(function(value) { return value.project })
          .map((projectGroup, projectName) => ({ 
                  name: projectName,
                  test: {
                      total:    _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Test')) , sum, 0),
                      totalDev: _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Test' && o.subArea === 'Dev')) , sum, 0),
                      totalQA:  _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Test' && o.subArea === 'QA')) , sum, 0),
                      totalSup: _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Test' && o.subArea === 'Support')) , sum, 0)
                  },
                 external: {
                      total:    _.reduce(_.filter(projectGroup, (o) => ( o.area === 'External')) , sum, 0),
                      totalDev: _.reduce(_.filter(projectGroup, (o) => ( o.area === 'External' && o.subArea === 'Dev')) , sum, 0),
                      totalQA:  _.reduce(_.filter(projectGroup, (o) => ( o.area === 'External' && o.subArea === 'QA')) , sum, 0),
                      totalSup: _.reduce(_.filter(projectGroup, (o) => ( o.area === 'External' && o.subArea === 'Support')) , sum, 0)
                  },
                  escalation: {
                      total:    _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Escalation')) , sum, 0),
                      totalDev: _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Escalation' && o.subArea === 'Dev')) , sum, 0),
                      totalQA:  _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Escalation' && o.subArea === 'QA')) , sum, 0),
                      totalSup: _.reduce(_.filter(projectGroup, (o) => ( o.area === 'Escalation' && o.subArea === 'Support')) , sum, 0)
                  }       
    }))
2
jando 12 Янв 2017 в 17:08

3 ответа

Лучший ответ

Понятия не имею, что вы хотите оптимизировать, lodash должен сделать это вместо вас. У меня есть только одно замечание

var sum = (total, item) => total += item.done;
function reduceData(projectGroup, projectName, comparationLabel) {
   return {
     total:    _.reduce(_.filter(projectGroup, (o) => ( o.area === comparationLabel)) , sum, 0),
     totalDev: _.reduce(_.filter(projectGroup, (o) => ( o.area === comparationLabel && o.subArea === 'Dev')) , sum, 0),
     totalQA:  _.reduce(_.filter(projectGroup, (o) => ( o.area === comparationLabel && o.subArea === 'QA')) , sum, 0),
     totalSup: _.reduce(_.filter(projectGroup, (o) => ( o.area === comparationLabel && o.subArea === 'Support')) , sum, 0)
   }
}
result = _.chain(data)
          .groupBy(function(value) { return value.project })
          .map((projectGroup, projectName) => ({ 
                  name: projectName,
                  test: reduceData(projectGroup, projectName, 'Test'),
                  external: reduceData(projectGroup, projectName, 'External'),
                  escalation: reduceData(projectGroup, projectName, 'Escalation')    
    }))

И да, вы можете избежать _.filter(projectGroup, (o), если будете делать какой-то кеш, но сэкономите небольшое количество ресурсов программы.

0
degr 12 Янв 2017 в 14:21

Почему бы не использовать один цикл в простом Javascript для группировки и суммирования часов?

Это предложение использует хеш-таблицу и группирует по значению свойства project.

var data = [{ project: "ABC", area: 'Test', subArea: 'Dev', done: 10 }, { project: "ABC", area: 'Test', subArea: 'QA', done: 10 }, { project: "ABC", area: 'Test', subArea: 'Support', done: 10 }, { project: "ABC", area: 'External', subArea: 'Dev', done: 5 }, { project: "ABC", area: 'External', subArea: 'QA', done: 5 }, { project: "ABC", area: 'External', subArea: 'Support', done: 5 }, { project: "ABC", area: 'Escalation', subArea: 'Dev', done: 20 }, { project: "ABC", area: 'Escalation', subArea: 'QA', done: 20 }, { project: "ABC", area: 'Escalation', subArea: 'Support', done: 20 }, { project: "ABC123", area: 'Test', subArea: 'Dev', done: 20 }, { project: "ABC123", area: 'Test', subArea: 'QA', done: 20 }, { project: "ABC123", area: 'Test', subArea: 'Support', done: 20 }, { project: "ABC123", area: 'External', subArea: 'Dev', done: 10 }, { project: "ABC123", area: 'External', subArea: 'QA', done: 10 }, { project: "ABC123", area: 'External', subArea: 'Support', done: 10 }, { project: "ABC123", area: 'Escalation', subArea: 'Dev', done: 5 }, { project: "ABC123", area: 'Escalation', subArea: 'QA', done: 5 }, { project: "ABC123", area: 'Escalation', subArea: 'Support', done: 5 }],
    result = [];

data.forEach(function (a) {
    var area = a.area.toLowerCase(),
        subArea = 'total' + a.subArea;

    if (!this[a.project]) {
        this[a.project] = { name: a.project };
        result.push(this[a.project]);
    }
    this[a.project][area] = this[a.project][area] || { total: 0 };
    this[a.project][area][subArea] = (this[a.project][area][subArea] || 0) + a.done;
    this[a.project][area].total += a.done;
}, Object.create(null));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0
Nina Scholz 12 Янв 2017 в 14:54

Вместо того, чтобы вложить так:

_.reduce(_.filter(projectGroup, ...), sum, 0)

Вы можете сделать это, чтобы предотвратить повторение цикла по коллекции дважды:

_(projectGroup).filter(...).reduce(sum, 0).value()
0
Mike Jerred 12 Янв 2017 в 14:24