读入数据时对重复值求和
Summing duplicate values while reading in data
我正在从 stream
中从上到下读取 5000 行数据,并将其存储在新的 CSV 文件中。
ProductCode |Name | Type | Price
ABC | Shoe | Trainers | 3.99
ABC | Shoe | Trainers | 4.99
ABC | Shoe | Trainers | 5.99
ABC | Shoe | Heels | 3.99
ABC | Shoe | Heels | 4.99
ABC | Shoe | Heels | 5.99
...
我希望 CSV 有一行但价格相加,而不是重复条目:
ProductCode |Name | Type | Price
ABC | Shoe | Trainers | 14.97
ABC | Shoe | Heels | 14.97
我将每一行存储为 Product
:
public class Product
{
public string ProductCode { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Price { get; set; }
}
从流中读取数据后,我得到一个 IEnumerable<Product>
。
我的代码是:
string fileName = Path.Combine(directory, string.Format("{0}.csv", name));
var results = Parse(stream).ToList(); //Parse returns IEnumerable<Product>
if (results.Any())
{
using (var streamWriter = File.CreateText(fileName))
{
//writes the header line out
streamWriter.WriteLine("{0},{1}", header, name);
results.ForEach(p => { streamWriter.WriteLine(_parser.ConvertToOutputFormat(p)); });
streamWriter.Flush();
streamWriter.Close();
}
Optional<string> newFileName = Optional.Of(SharpZipWrapper.ZipFile(fileName, RepositoryDirectory));
//cleanup
File.Delete(fileName);
return newFileName;
}
我不想再次遍历 5000 行以删除重复项,但想在将条目添加到 csv 文件之前检查该条目是否已存在。
最有效的方法是什么?
听起来您只需要适当的 LINQ 转换:
results = results
.GroupBy(p => p.ProductCode)
.Select(g => new Product {
ProductCode = g.Key,
Name = g.First().Name,
Type = g.First().Type,
Price = g.Sum(p => p.Price)
})
.ToList();
或者如果您的 ProductCode
由于某些奇怪的原因不是唯一 ID:
results = results
.GroupBy(p => new { p.ProductCode, p.Name, p.Type })
.Select(g => new Product {
ProductCode = g.Key.ProductCode,
Name = g.Key.Name,
Type = g.Key.Type,
Price = g.Sum(p => p.Price)
})
.ToList();
不过,这是假设您已经将 Product
类型更改为 decimal
Price
属性 类型。价格不是文本,因此不应存储为字符串。
List<Product> results = new List<Product>(new Product[]{
new Product() { ProductCode="ABC ", Name="Shoe", Type="Trainers", Price="3.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Trainers", Price="4.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Trainers", Price="5.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Heels", Price="3.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Heels", Price="4.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Heels", Price="5.99" },
});
results = (from e in results
group e by new { e.ProductCode, e.Name, e.Type } into g
select new Product
{
ProductCode = g.Key.ProductCode,
Name = g.Key.Name,
Type = g.Key.Type,
Price = g.Sum(p => double.Parse(p.Price, CultureInfo.InvariantCulture)).ToString("0.00", CultureInfo.InvariantCulture)
}).ToList();
您可以创建一个 class 字典,其中包含产品代码键和产品值。
您也可以逐行阅读流,尝试将新的 key/value 对添加到字典中。但在添加值之前,您检查它是否包含密钥(产品代码),如果是,您将获得该密钥的 Product 对象并更新价格。
然后您遍历字典并写入 csv。
这样您就不需要在写入 CSV 之前阅读两次来查找重复项。
I don't want to go through the 5000 rows again to remove the duplicates but would like to check if the entry already exists before I add it to the csv file.
为了实现这个,你可以覆盖Product
对象上的Equals()
,然后在添加两次之前检查Product
是否存在于列表中,然后总结Price
相反。
在这里,您可以在覆盖 Equals():
时找到一些准则
Guidelines for Overloading Equals() and Operator == (C# Programming Guide)
我正在从 stream
中从上到下读取 5000 行数据,并将其存储在新的 CSV 文件中。
ProductCode |Name | Type | Price
ABC | Shoe | Trainers | 3.99
ABC | Shoe | Trainers | 4.99
ABC | Shoe | Trainers | 5.99
ABC | Shoe | Heels | 3.99
ABC | Shoe | Heels | 4.99
ABC | Shoe | Heels | 5.99
...
我希望 CSV 有一行但价格相加,而不是重复条目:
ProductCode |Name | Type | Price
ABC | Shoe | Trainers | 14.97
ABC | Shoe | Heels | 14.97
我将每一行存储为 Product
:
public class Product
{
public string ProductCode { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Price { get; set; }
}
从流中读取数据后,我得到一个 IEnumerable<Product>
。
我的代码是:
string fileName = Path.Combine(directory, string.Format("{0}.csv", name));
var results = Parse(stream).ToList(); //Parse returns IEnumerable<Product>
if (results.Any())
{
using (var streamWriter = File.CreateText(fileName))
{
//writes the header line out
streamWriter.WriteLine("{0},{1}", header, name);
results.ForEach(p => { streamWriter.WriteLine(_parser.ConvertToOutputFormat(p)); });
streamWriter.Flush();
streamWriter.Close();
}
Optional<string> newFileName = Optional.Of(SharpZipWrapper.ZipFile(fileName, RepositoryDirectory));
//cleanup
File.Delete(fileName);
return newFileName;
}
我不想再次遍历 5000 行以删除重复项,但想在将条目添加到 csv 文件之前检查该条目是否已存在。
最有效的方法是什么?
听起来您只需要适当的 LINQ 转换:
results = results
.GroupBy(p => p.ProductCode)
.Select(g => new Product {
ProductCode = g.Key,
Name = g.First().Name,
Type = g.First().Type,
Price = g.Sum(p => p.Price)
})
.ToList();
或者如果您的 ProductCode
由于某些奇怪的原因不是唯一 ID:
results = results
.GroupBy(p => new { p.ProductCode, p.Name, p.Type })
.Select(g => new Product {
ProductCode = g.Key.ProductCode,
Name = g.Key.Name,
Type = g.Key.Type,
Price = g.Sum(p => p.Price)
})
.ToList();
不过,这是假设您已经将 Product
类型更改为 decimal
Price
属性 类型。价格不是文本,因此不应存储为字符串。
List<Product> results = new List<Product>(new Product[]{
new Product() { ProductCode="ABC ", Name="Shoe", Type="Trainers", Price="3.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Trainers", Price="4.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Trainers", Price="5.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Heels", Price="3.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Heels", Price="4.99" },
new Product() { ProductCode="ABC ", Name="Shoe", Type="Heels", Price="5.99" },
});
results = (from e in results
group e by new { e.ProductCode, e.Name, e.Type } into g
select new Product
{
ProductCode = g.Key.ProductCode,
Name = g.Key.Name,
Type = g.Key.Type,
Price = g.Sum(p => double.Parse(p.Price, CultureInfo.InvariantCulture)).ToString("0.00", CultureInfo.InvariantCulture)
}).ToList();
您可以创建一个 class 字典,其中包含产品代码键和产品值。
您也可以逐行阅读流,尝试将新的 key/value 对添加到字典中。但在添加值之前,您检查它是否包含密钥(产品代码),如果是,您将获得该密钥的 Product 对象并更新价格。
然后您遍历字典并写入 csv。 这样您就不需要在写入 CSV 之前阅读两次来查找重复项。
I don't want to go through the 5000 rows again to remove the duplicates but would like to check if the entry already exists before I add it to the csv file.
为了实现这个,你可以覆盖Product
对象上的Equals()
,然后在添加两次之前检查Product
是否存在于列表中,然后总结Price
相反。
在这里,您可以在覆盖 Equals():
时找到一些准则
Guidelines for Overloading Equals() and Operator == (C# Programming Guide)