LINQ 惰性内连接
LINQ Lazy Inner Join
有没有办法利用 LINQ API 以惰性方式(内部)加入集合?我面临这样一种情况,我需要在级联中关联多个 CSV,并且如果“上游”CSV 结果为空,我想避免不必要的解析操作:
ReadCsvRows(fileA).Join(
ReadCsvRows(fileB), // why to parse B if A is empty?!
...
).Join(
ReadCsvRows(fileC),
...
).Join(...)
请注意,ReadCsvRows
方法位于接口之后,唯一的要求是该方法必须 return IEnumerable
(如 here 所示)。为了让事情“流畅”,我可以通过引入自定义扩展方法来解决这个问题
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
this IEnumerable<TOuter> outer,
Func<IEnumerable<TInner>> innerFunc,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector) =>
!outer.Any() ?
Array.Empty<TResult>() :
outer.Join(
innerFunc(),
outerKeySelector,
innerKeySelector,
resultSelector
);
但想知道我对 vanilla LINQ 有哪些选择 API。
如果我是你,我会将 ReadCsvRows(fileB) 和 ReadCsvRows(fileC) 分成两个单独的变量。并添加一个“If”条件来确定是否应该执行“ReadCSVRows(fileB)”。
像这样:
List<FileATypeA> fileAData = ReadCsvRows(fileA);
List<FileATypeB> fileBData = new List<FileATypeB>();
List<FileATypeC> fileCData = new List<FileTypeC>();
if(fileAData.Count() > 0)
fileBData = ReadCsvRows(fileB);
if(fileBData.Count() > 0)
fileCData = ReadCsvRows(fileC);
List<FinalType> final = from a in fileAData
join b in fileBData
on a.Key = b.Key
join c in fileCData
on b.Key = c.Key
看看这是否有帮助
您不需要自己的 Join
实现。如果 outer
没有记录,标准 System.Linq
实现不会枚举 inner
序列。
因此,请检查您的 ReadCsvRows
实施。如果您在它的主体中使用 yield return
,则不会出现不需要的读取。
示意图:
public static IEnumerable<Row> ReadCsvRows(string fileName)
{
using var reader = new CsvReader(fileName);
while (reader.ReadNext())
{
yield return reader.CurrentRow;
}
}
在这种情况下,如果第一个序列没有行,甚至 new CsvReader(fileName)
也不会被调用。
有没有办法利用 LINQ API 以惰性方式(内部)加入集合?我面临这样一种情况,我需要在级联中关联多个 CSV,并且如果“上游”CSV 结果为空,我想避免不必要的解析操作:
ReadCsvRows(fileA).Join(
ReadCsvRows(fileB), // why to parse B if A is empty?!
...
).Join(
ReadCsvRows(fileC),
...
).Join(...)
请注意,ReadCsvRows
方法位于接口之后,唯一的要求是该方法必须 return IEnumerable
(如 here 所示)。为了让事情“流畅”,我可以通过引入自定义扩展方法来解决这个问题
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
this IEnumerable<TOuter> outer,
Func<IEnumerable<TInner>> innerFunc,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector) =>
!outer.Any() ?
Array.Empty<TResult>() :
outer.Join(
innerFunc(),
outerKeySelector,
innerKeySelector,
resultSelector
);
但想知道我对 vanilla LINQ 有哪些选择 API。
如果我是你,我会将 ReadCsvRows(fileB) 和 ReadCsvRows(fileC) 分成两个单独的变量。并添加一个“If”条件来确定是否应该执行“ReadCSVRows(fileB)”。
像这样:
List<FileATypeA> fileAData = ReadCsvRows(fileA);
List<FileATypeB> fileBData = new List<FileATypeB>();
List<FileATypeC> fileCData = new List<FileTypeC>();
if(fileAData.Count() > 0)
fileBData = ReadCsvRows(fileB);
if(fileBData.Count() > 0)
fileCData = ReadCsvRows(fileC);
List<FinalType> final = from a in fileAData
join b in fileBData
on a.Key = b.Key
join c in fileCData
on b.Key = c.Key
看看这是否有帮助
您不需要自己的 Join
实现。如果 outer
没有记录,标准 System.Linq
实现不会枚举 inner
序列。
因此,请检查您的 ReadCsvRows
实施。如果您在它的主体中使用 yield return
,则不会出现不需要的读取。
示意图:
public static IEnumerable<Row> ReadCsvRows(string fileName)
{
using var reader = new CsvReader(fileName);
while (reader.ReadNext())
{
yield return reader.CurrentRow;
}
}
在这种情况下,如果第一个序列没有行,甚至 new CsvReader(fileName)
也不会被调用。