2015-04-21 48 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

相關問題