我有30 000行csv文件。我必须根据许多条件选择许多值,所以我决定使用linq来代替许多循环和"if’s"。我曾经写过类来读csv。它实现了与linq一起使用的IEnumerable。这是我的枚举数:
class CSVEnumerator : IEnumerator
{
private CSVReader _csv;
private int _index;
public CSVEnumerator(CSVReader csv)
{
_csv = csv;
_index = -1;
}
public void Reset(){_index = -1;}
public object Current
{
get
{
return new CSVRow(_index,_csv);
}
}
public bool MoveNext()
{
return ++_index < _csv.TotalRows;
}
}
它在工作,但是很慢。假设我想在列A中选择100;150行的最大值。
max = (from CSVRow r in csv where r.ID > 100 && r.ID < 150 select r).Max(y=>y["A"]);
这将工作,但linq搜索最大值在30 000行,而不是48。正如我所说,我可以使用循环,但只有在这个例子中,条件是"残酷的":)
是否有办法覆盖linq集合搜索。类似于:查看我的枚举器上使用的查询,查看"where"中的任何linq条件是否包含"行ID过滤器",并基于此给出另一个数据。
我不想将部分数据复制到另一个数组/集合,问题不在我的csv阅读器中。通过id访问每一行很快,唯一的问题是当您访问所有30,000行时。感谢任何帮助:-)
如果你希望能够有效地使用LINQ,你需要使用表达式树,以一种类似(但更简单)的方式,而不是各种LINQ提供程序为SQL数据库所做的。虽然可行,但我认为对于这样一个简单的任务来说,这将是相当多的代码。
因此,我认为一个更好的解决方案是使用一个单独的方法来选择你想要的行(然后可能使用LINQ来处理结果)。
而且,许多返回集合的操作(包括你的原始代码和我的修改)可以通过使用迭代器方法来简化。
所以,你的代码看起来像这样:public static IEnumerable<CSVRow> GetRows(
this CSVReader reader, int idGreaterThan, int idLessThan)
{
for (int i = idGreaterThan + 1; i < idLessThan; i++)
{
yield return new CSVRow(reader, i);
}
}
这里,它是CSVReader
的一个扩展方法,但另一个解决方案(例如该类的实际方法)可能更适合您。
你的例子看起来像这样:
max = csvReader.GetRows(100, 150).Max(y => y["A"]);
(另外,我觉得奇怪的是,当您有100和150的限制时,您实际上想要101和149之间的行。但我假设你有理由这样做,所以我也这样做了
就LINQ而言,r.ID只是一个被过滤的值,因此所有30k行都被考虑用于Max操作。如果这是一个行索引,这里似乎就是这种情况,您可以使用Skip和Take来避免比较所有30k行。
max = csv.Skip(100).Take(50).Max(y => y["A"]);
@DougM关于求值的顺序是正确的,但在这种情况下,我要做的是在初始化时一次性命中并生成对任何"索引"字段的查找:基本上,预先计算行索引到行的映射(字典)。也就是说,只有当您对给定的索引字段有许多重复查询时,这才有用。