从 table 数据中删除列的最佳方法是什么?
Whats the best way to remove a column from a table 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}
]
我想要做的是删除一列,然后添加具有匹配列的所有行的 'numEaten'。想象一下:您实际上并不关心 何时 吃水果,您只想知道谁吃了多少。所以输出 table 看起来像:
[
{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()
用于 而不是 column
或 aggregateColumn
的每一列,但它似乎有点老套让那个工作。有没有更好的方法?
编辑
似乎没有针对此解决方案的单一解决方案:在结合以下解决方案的反馈后,发布我的想法。请注意,与原始问题不同,这会将列保留,而不是删除,并且适用于任何模式。
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;
});
}
事实上你在谈论 "columns" 表明你有一个 table 而实际上你正在处理一个字符串映射数组。
由于 JavaScript 是基于原型的,因此您的问题没有 "beautiful" 或开箱即用的解决方案(不仅如此,而且)。
您可以在 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 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}
]
然后,通过加入 name
和 fruit
创建群组。
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},
]
纯 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});
使用 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))]));
});
}
考虑以下数据:
[
{ 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'。想象一下:您实际上并不关心 何时 吃水果,您只想知道谁吃了多少。所以输出 table 看起来像:
[
{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()
用于 而不是 column
或 aggregateColumn
的每一列,但它似乎有点老套让那个工作。有没有更好的方法?
编辑
似乎没有针对此解决方案的单一解决方案:在结合以下解决方案的反馈后,发布我的想法。请注意,与原始问题不同,这会将列保留,而不是删除,并且适用于任何模式。
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;
});
}
事实上你在谈论 "columns" 表明你有一个 table 而实际上你正在处理一个字符串映射数组。
由于 JavaScript 是基于原型的,因此您的问题没有 "beautiful" 或开箱即用的解决方案(不仅如此,而且)。
您可以在 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 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}
]
然后,通过加入 name
和 fruit
创建群组。
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},
]
纯 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});
使用 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))]));
});
}