Как агрегировать массив свойств объекта на основе двух критериев внутри каждого объекта - PullRequest
0 голосов
/ 03 ноября 2018

Допустим, у меня есть этот массив объектов:

[{
  a: "test", b: "5", c: 4
},
{
  a: "test 2", b: "2", c: 10
},
{
  a: "test", b: "5", c: 66
}]

Я хочу объединить объекты, где свойства a & b равны в каждом объекте, поэтому я получаю:

[{
   a: "test", b: "5", c: 70
},
{
   a: "test 2", b: "2", c: 10
}]

Я могу справиться с этим в нескольких циклах for, например.

const a = [];
for(let item of data) {
    const dateTime  = new Date(item.T);
    const time = dateTime.getHours()+":"+dateTime.getMinutes()+":"+dateTime.getSeconds();
    const id = item.p+time+item.m;

    if (a[id]) {
        a[id].q = (parseFloat(a[id].q) + parseFloat(item.q)).toFixed(8)
    } else {
        a[id] = item;
    }
}
const b = [];
for (var key in a) { 
    b.push(a[key]); // converts to numbered array
}
return b;

... но мне было интересно, есть ли более оптимальный путь?

Ответы [ 2 ]

0 голосов
/ 03 ноября 2018

Есть более абстрактный способ сделать это с reduce, но я просто чешу свой мозг здесь.

const input = [
    {
        a : "test", b : "5", c : 4
    },
    {
        a : "test 2", b : "2", c : 10
    },
    {
        a : "test", b : "5", c : 66
    }
];

/**
 * Will sum all fields except the one specified in fields, which will group
 *
 * @param input    The input array
 * @param fields   The fields that needs to be grouped
 * @returns {*}
 */
function sumGroup( input, fields ) {
    return input.reduce( ( result, item ) => {

        /** Get all keys of the current item ... ["a","b","c"...] */
        const itemKeys = Object.keys( item );

        /** Get the grouped item that was already stored */
        let currentItem = result.find( element => {
            return fields.map( field => element[field] === item[field] )
                         .indexOf( false ) === -1;
        } );

        /** If there was no group item, we create one and add it */
        if ( !currentItem ) {

            currentItem = itemKeys.filter( key => fields.indexOf( key ) > -1 )
                                  .reduce( ( obj, key ) => Object.assign( obj, {
                                      [key] : item[key]
                                  } ), {} );

            result.push( currentItem );

        }

        /**
         * Finally we sum all other keys and add them to the already referenced
         * current item
         */
        itemKeys.filter( key => fields.indexOf( key ) === -1 )
                .forEach( key => {
                    if ( !currentItem[key] ) { currentItem[key] = 0; }
                    currentItem[key] = currentItem[key] + item[key];
                } );

        return result;

    }, [] );
}

console.log( sumGroup( input, ["a", "b"] ) );
0 голосов
/ 03 ноября 2018

ОБНОВЛЕНИЕ :

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

const data = [
  { a: "test", b: "5", c: 4 },
  { a: "test 2", b: "2", c: 10 },
  { a: "test", b: "5", c: 66 }
]

function agregate(arr) {
  const result = [];
  const cache = {};
  const len = arr.length;

  for (let i = 0; i < len; ++i) {
    const item = arr[i];
    const itemC = item.c;
    const key = JSON.stringify({ a: item.a, b: item.b });

    // If is already cached, just agregates the c property.
    if (cache[key]) {
      cache[key][0] += itemC;
      result[cache[key][1]].c += itemC;
    } else {
      // Pushes into result and updates the cache {key} = [c value, index].
      result.push(item);
      cache[key] = [itemC, result.length - 1];
    }
  }

  return result;
}

console.time('Agregate');
console.log(agregate(data));
console.timeEnd('Agregate');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...