Javascript 从表数据中删除列的最佳方法是什么?

Javascript 从表数据中删除列的最佳方法是什么?,javascript,underscore.js,Javascript,Underscore.js,考虑以下数据: [ { time: '5:00', name: 'bran', fruit: 'pear', numEaten: 3}, { time: '5:00', name: 'rickon', fruit: 'apple', numEaten: 2}, { time: '6:00', name: 'bran', fruit: 'apple', numEaten: 5}, { time: '6:00', name: 'rickon', fruit: 'grape', num

考虑以下数据:

[
  { time: '5:00', name: 'bran', fruit: 'pear', numEaten: 3},
  { time: '5:00', name: 'rickon', fruit: 'apple', numEaten: 2},
  { time: '6:00', name: 'bran', fruit: 'apple', numEaten: 5},
  { time: '6:00', name: 'rickon', fruit: 'grape', numEaten: 1},
  { time: '6:00', name: 'bran', fruit: 'pear', numEaten: 2},
  { time: '6:00', name: 'eddard', fruit: 'pear', numEaten: 2},
  { time: '7:00', name: 'rickon', fruit: 'apple', numEaten: 7}
]
我要做的是删除一列,并添加所有具有匹配列的行的“numEaten”。想象一下:你并不在乎什么时候吃水果,你只想知道谁吃了多少。因此,输出表如下所示:

[
  {name: 'bran', fruit: 'pear', numEaten: 5},
  {name: 'bran', fruit: 'apple', numEaten: 2},
  {name: 'rickon', fruit: 'apple', numEaten: 9},
  {name: 'rickon', fruit: 'grape', numEaten: 1},
  {name: 'eddard', fruit: 'pear', numEaten: 2},
]
我一直在用下划线查看各种javascript数组原型函数和扩展,但我看不到一种特别优雅的方法来实现这一点。我想要一个原型功能:

function aggregate(data, column, aggregateColumn) // aggregate(data, 'time', 'numEaten')
这将执行此操作。从概念上讲,我考虑对不是
聚合列
的每一列运行
.groupBy()。有更好的办法吗

编辑

似乎没有一个单一的解决方案:在整合了以下解决方案的反馈后,发布我的想法。注意:与原始问题不同,这需要保留列,而不是删除列,并且适用于任何模式

  var aggregate = function(data, aggregateColumn, keepColumns) {
    keepColumns = keepColumns || [];
    if(!Array.isArray(keepColumns)) {
      keepColumns = [ keepColumns ];
    }

    var removeColumns = _.difference(_.keys(data[0]), keepColumns.concat(aggregateColumn));
    var grouped = _.groupBy(data, function(d) {
      return _.reduce(keepColumns, function(o, col) {
        return o + d[col] + '-';
      }, '');      
    });

    return _.map(grouped, function(mapData) {
      var reduced = _.reduce(keepColumns, function(o, col) {
          o[col] = mapData[0][col];
          return o;
        }, {}
      );

      reduced[aggregateColumn] = _.reduce(mapData, function(o, aggrData) {
          return o + aggrData[aggregateColumn];
        }, 0
      );

      return reduced;
    });
  }

您所说的“列”这一事实表明,实际上您在处理字符串映射数组时,脑子里有一个表。
由于JavaScript是基于原型的,所以您的问题没有“漂亮”或现成的解决方案(不仅如此,而且)

您可以在for循环和Array.forEach之间进行选择。我更喜欢前者。
另外,我在这里返回一个新数组,而不是修改原来的数组

function aggregate(data, column, aggregateColumn)
{
    var array = [];
    // Just work the array
    for(var i = 0; i < data.length; i++)
    {
        var currentOld = data[i];
        var found = false;
        // Label the loop, so we can control it
        outside:
        // Check if the current type already exists in the new array
        for(var j = 0; j < array.length; j++)
        {
            var currentNew = array[j];
            // Check if all properties match
            for(var property in currentOld)
            {
                // Skip properties that match column or aggregateColumn
                if(property == column || property == aggregateColumn)
                {
                    continue;
                }
                // Now check if their values match
                if(currentOld[property] != currentNew[property])
                {
                    // If they don't match, continue the outer loop
                    continue outside;
                }
            }
            // At this point, all properties matched, so we aggregate
            currentNew[aggregateColumn] += currentOld[aggregateColumn];
            // Set the flag to indicate that we found it
            found = true;
            // And end the loop
            break;
        }
        // If the current type is not yet in the new array, we need to put it there
        if(!found)
        {
            // Create a copy of it (assuming your data are trivial objects)
            var copy = JSON.parse(JSON.stringify(currentOld));
            // Remove your "column"
            delete copy[column];
            // And add it
            array.push(copy);
        }
    }
    return array;
}
函数聚合(数据、列、聚合列)
{
var数组=[];
//只需操作数组
对于(变量i=0;i
测试函数会输出您想要的相同数组,只是顺序不同,因为它保留了原始数组的顺序,而不是对其进行排序。

我想您知道如何对数组进行排序。;)

下面是一种在下划线中执行此操作的方法

让我们定义初始数据,如

var data = [
  { time: '5:00', name: 'bran', fruit: 'pear', numEaten: 3},
  { time: '5:00', name: 'rickon', fruit: 'apple', numEaten: 2},
  { time: '6:00', name: 'bran', fruit: 'apple', numEaten: 5},
  { time: '6:00', name: 'rickon', fruit: 'grape', numEaten: 1},
  { time: '6:00', name: 'bran', fruit: 'pear', numEaten: 2},
  { time: '6:00', name: 'eddard', fruit: 'pear', numEaten: 2},
  { time: '7:00', name: 'rickon', fruit: 'apple', numEaten: 7}
]
然后,通过加入
名称
水果
创建组

var groups = _.groupBy(data, function(value){
        return value.name+ '#' + value.fruit;
    });
稍后在聚合时,我们将使用此自定义的
sum
函数

function sum(numbers) {
    return _.reduce(numbers, function(result, current) {
        return result + parseFloat(current);
    }, 0);
}
现在,
map
通过提取
numEaten
并获取组的
sum

var out = _.map(groups, function(group){
        return {
            name: group[0].name,
            fruit: group[0].fruit,
            numEaten: sum(_.pluck(group, 'numEaten'))
        }
    });
最后,我们得到如下输出--


一个通用的解决方案将很容易与纯JavaScript,但我想提供这个解决方案使用下划线,因为它有时感到兴奋

由于下划线没有提供适当的函数来删除重复项,因此我使用了
.uniq
JSON.stringify
函数混合使用

下面是成功测试的
聚合
函数

  var objs = [
    { time: '5:00', name: 'bran', fruit: 'pear', numEaten: 3},
    { time: '6:00', name: 'bran', fruit: 'pear', numEaten: 2},  
    { time: '6:00', name: 'bran', fruit: 'apple', numEaten: 5},  
    { time: '5:00', name: 'rickon', fruit: 'apple', numEaten: 2},
    { time: '7:00', name: 'rickon', fruit: 'apple', numEaten: 7},  
    { time: '6:00', name: 'rickon', fruit: 'grape', numEaten: 1},  
    { time: '6:00', name: 'eddard', fruit: 'pear', numEaten: 2}
    ];

function aggregate(data, column, aggregateColumn){
var res=[];
_.map(data, function(item){
            var comparer={},
                compared={};

            for(var k in item){
                if(k!=column){
                compared[k]=item[k];
                if(k!=aggregateColumn)
                    comparer[k]=item[k];                    
                }
            }
_.each(_.where(_.without(data,item), comparer),function(aggregable){                    
                compared[aggregateColumn]+=aggregable[aggregateColumn];
                return compared;
                });
                res.push(compared);
            });
    return _.uniq(res,function(item){return JSON.stringify(item);})
}

    ///usage
    var o=aggregate(objs, 'time', 'numEaten');
    console.log({'o':o});

使用John Galt的优秀答案中的求和函数,这里是一个通用版本

function aggregate(data, aggregateColumn, keepColumns){

   var groups = _.groupBy(data, function(item){
      return _.values(_.pick(item, keepColumns)).join('#')
   });

   return _.map(groups, function(group){
       return _.extend( _.pick(group[0], keepColumns), 
          _.object([aggregateColumn], [sum(_.pluck(group, aggregateColumn))]));
   }); 
}

{name:'bran',fruit:'apple',numEaten:2},
的输出应该是numEaten:5?nerdy observation-br–n is crow in welsh这与我最终得出的结果非常接近,尽管我使用了所有其他列的构造键,对结果中的每个值进行了分组,然后进行了reduce。不过我觉得你的有点干净!如果这回答了你的问题,你会接受的。。。有时您希望使用
delete
操作符从对象中删除属性!此解决方案可能有效,但似乎不通用!!似乎集合中的对象必须具有“fruit”和“name”属性…而op要求使用一个带有可移动和聚合列作为参数的函数…非常好!从中了解了一些新的下划线功能:)
function aggregate(data, aggregateColumn, keepColumns){

   var groups = _.groupBy(data, function(item){
      return _.values(_.pick(item, keepColumns)).join('#')
   });

   return _.map(groups, function(group){
       return _.extend( _.pick(group[0], keepColumns), 
          _.object([aggregateColumn], [sum(_.pluck(group, aggregateColumn))]));
   }); 
}