Агрегировать и преобразовывать массив объектов

Используя методы функционального программирования javascript (например, map/reduce), как можно агрегировать/подсчитывать поле status из arr1 и преобразовывать его в массив объектов ключ/значение в arr2.

arr1 = [
  {'task':'do something 1', 'status':'done'} , 
  {'task':'do something 2', 'status':'done'} , 
  {'task':'do something 3', 'status':'pending'} , 
  {'task':'do something 4', 'status':'done'}
];

// Aggregate arr1 `status` field and transform to:

arr2 = [
  {key:'done', value: 3},
  {key:'pending', value: 1}
];

Вот мое частичное решение WIP, которое обрабатывает только часть агрегации. Мне все еще нужна часть преобразования.

var arr2 = arr1.map(function(item) {
    return item.status;
  }).reduce(function(acc,curr,idx){
    if(acc[curr] === undefined) acc[curr] = 1;
    else acc[curr] += 1;
    return acc;
  }, []); 

person jboothe    schedule 03.02.2016    source источник
comment
Я бы просто сделал это, но не уверен, что это достаточно функционально.   -  person adeneo    schedule 03.02.2016
comment
Ваше решение не дает ожидаемого ответа. Или предоставленный ответ неверен.   -  person Kiril    schedule 03.02.2016
comment
Я считаю, что @Kiril прав, опубликованный код создает [done: 3, pending: 1], или, другими словами, он обрабатывает массив как объект и не возвращает ожидаемый результат?   -  person adeneo    schedule 03.02.2016
comment
@adeneo, твое решение отличное. Почему бы не опубликовать это как ответ?   -  person jboothe    schedule 03.02.2016
comment
@jboothe - не был уверен, что вы этого хотели, но я опубликую, если для потомков   -  person adeneo    schedule 03.02.2016
comment
есть функциональные библиотеки, которые сделают эту задачу очень простой   -  person Cory Danielson    schedule 03.02.2016


Ответы (3)


Вот самый функциональный способ, который я смог придумать. Это включает в себя как агрегацию, так и желаемое преобразование:

var arr2 = Object.keys(arr2 = arr1.map(function(item) {
    return item.status;
}).reduce(function(acc,curr){
    acc[curr] = acc[curr] + 1 || 1;
    return acc;
}, [])).map(function(item){
    return {key: item, value: arr2[item]}
});
person Alex Alksne    schedule 03.02.2016
comment
Алекс, очень красиво сделано. Единственная очистка, которую я бы сделал, - это мой собственный код - объединить строки 4 и 5 в троичную acc[curr] === undefined ? acc[curr] = 1 : acc[curr] + 1; - person jboothe; 03.02.2016
comment
@jboothe хорошее замечание! Я тоже думал о троичном, но придумал немного более компактное решение, как вы думаете? - person Alex Alksne; 03.02.2016
comment
Изящный. неопределенный + 1 или 1. - person jboothe; 03.02.2016

Вы можете попробовать Array.prototype.forEach(). Также вместо использования массива вы можете использовать object. Это избавит вас от необходимости зацикливаться на подсчете определенного статуса.

arr1 = [
  {'task':'do something 1', 'status':'done'} , 
  {'task':'do something 2', 'status':'done'} , 
  {'task':'do something 3', 'status':'pending'} , 
  {'task':'do something 4', 'status':'done'}
];

var result = {};
arr1.forEach(function(item){
  if(!result[item.status])
    result[item.status] = 0;
  
  result[item.status]++;
});
console.log(result);

person Rajesh    schedule 03.02.2016

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

arr2 = [];
arr1.forEach(function(item){
  var keyPresent = false;
  for(var i = 0, len = arr2.length; i < len; i++) {
      if( arr2[ i ].key === item.status ){
         keyPresent = true;
         arr2[ i ].value++
      }
  }
  if(!keyPresent){
    arr2.push({key: item.status , value : 1})
}

Результат, заданный

arr2 = [
  {key:'done', value: 3},
  {key:'pending', value: 1}
];
person Nighisha John    schedule 03.02.2016