2015-04-21 47 views
4

考虑以下数据:什么是从表格数据中删除列的最佳方法?

[ 
    { 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') 

将执行此操作。从概念上讲,我正在考虑为而不是columnaggregateColumn的每一列运行_.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; 
    }); 
    } 
+0

为'{名的输出中: '麸皮',水果:'苹果',numEaten:2},'应该是** numEaten:5 **? – Zero

+1

书呆子的观察 - brân在威尔士的乌鸦 –

回答

2

下面是做到这一点的下划线

一种方式让我们定义的初始数据,如

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} 
] 

然后,通过加入他们创建一个基于namefruit组。

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')) 
     } 
    }); 

而且,最后我们有像输出 -

out 
[ 
    {name: 'bran', fruit: 'pear', numEaten: 5}, 
    {name: 'bran', fruit: 'apple', numEaten: 5}, 
    {name: 'rickon', fruit: 'apple', numEaten: 9}, 
    {name: 'rickon', fruit: 'grape', numEaten: 1}, 
    {name: 'eddard', fruit: 'pear', numEaten: 2}, 
] 
+0

这种解决方案可能工作,但它似乎并不通用!似乎集合中的对象必须具有“水果”和“名称”属性...而op要求具有可移除和聚合列作为参数的功能... – Bellash

1

,你在说“栏目”的事实表明,你有一个表记时,其实你处理字符串映射的数组。
由于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; 
} 

测试函数输出您希望的同一个数组,只是以不同的顺序输出,因为它保持原始数组的顺序而不是排序它。
我假设你知道如何排序数组。 ;)

2

一个通用的解决方案将是容易纯粹的JavaScript,但我想提供这种解决方案使用下划线,因为它有时令人兴奋!

由于下划线不提供适当的功能来删除重复项,我使用_.uniq混合JSON.stringify函数。

下面是测试成功aggregate功能

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}); 

Have a look at this Fiddle

+0

这非常接近我最终想出的结果,尽管我使用了所有*其他*列的构造键,然后对结果中的每个值进行了减少。我认为你的身体虽然有点干净! – Rollie

+0

如果这回答了你的问题你会接受它...有时候你想用'delete'操作符从一个对象中删除一个属性! – Bellash

1

使用从约翰高尔特的出色答卷总和功能,这里有一个通用版本

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))])); 
    }); 
} 
+0

非常好!从这里学习了一些新的下划线功能:) – Rollie

相关问题