LINQ遇到问题时,请在L​​ist< FileInfo> [英] Having trouble with LINQ left join on List<FileInfo>

查看:87
本文介绍了LINQ遇到问题时,请在L​​ist< FileInfo>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个List<FileInfo>列表,SourceFilesDestFiles.我想构建一个LINQ查询,该查询将返回文件名在Source中但不在Dest中的项目的列表,即左联接.

I have two List<FileInfo> lists, SourceFiles and DestFiles. I want to build a LINQ query that will return a list of the items whose filenames are in Source but not in Dest, i.e. a left join.

我为SourceFiles设置的数据是:

folder1\a.txt
folder1\b.txt
folder1\c.txt
folder1\d.txt

DestFiles是:

folder2\a.txt
folder2\b.txt
folder2\c.txt

因此查询应返回folder1\d.txt.

按照 MSDN示例,我尝试使用LINQ语法:

Following the MSDN example, I've tried using LINQ syntax:

var queryX = from s in SourceFiles
             join d in DestFiles
             on s.Name equals d.Name
             into SourceJoinDest
             from joinRow in SourceJoinDest.DefaultIfEmpty()
             select new
             {
                 joinRow.FullName
             };

并使用扩展方法:

var query = SourceFiles.GroupJoin(DestFiles,
                                    source => source.Name,
                                    dest => dest.Name,
                                    (source,dest) => new
                                    {
                                        path = source.FullName
                                    }).Select(x => x.path.DefaultIfEmpty())

但是这些都不起作用; LINQ语法版本返回Object reference not sent to an instance of an object,扩展版本返回Enumeration yielded no results.

But neither one of these work; the LINQ syntax version returns Object reference not sent to an instance of an object and the extension version returns Enumeration yielded no results.

我意识到这些查询仅返回FullName属性的集合,而不返回完整的FileInfo对象;我有接受每个FullName并返回FileInfo的代码,并针对查询中的每个项目执行此操作以重建列表.但是,如果有一种方法可以直接从查询中返回FileInfo,那就太好了.

I realize that these queries are only returning sets of FullName properties and not the full FileInfo objects; I have code that takes each FullName and returns a FileInfo, and does this for each item in the query to rebuild the list. But if there's a way to return a FileInfo directly from the query, that would be great.

推荐答案

我认为Join不是这里的理想工具.基本上,您正在寻找Except.内置的Except没有通过lambda指定属性的重载.您将必须创建自己的IEqualityComparer.您可以这样做,但是像这样:

I don't think Join is the ideal tool here. Basically you're looking for an Except. The built in Except doesn't have the overload to specify your properties through lambda. You will have to create your own IEqualityComparer. You could do it, however, like this:

var excepts = SourceFiles.Where(c => !DestFiles.Any(p => p.Name == c.Name)).ToList();

或者,要仅选择完整路径,可以在末尾使用Select.

Or, to select just the full path, you can use Select at the end.

var excepts = SourceFiles.Where(c => !DestFiles.Any(p => p.Name == c.Name))
                         .Select(f => f.FullName).ToList();


我建议使用扩展方法来快速执行ExceptIntersect.

public static IEnumerable<U> Except<R, S, T, U>(this IEnumerable<R> mainList, 
                                                IEnumerable<S> toBeSubtractedList,
                                                Func<R, T> mainListFunction, 
                                                Func<S, T> toBeSubtractedListFunction,
                                                Func<R, U> resultSelector)
{
    return EnumerateToCheck(mainList, toBeSubtractedList, mainListFunction, 
                            toBeSubtractedListFunction, resultSelector, false);
}

static IEnumerable<U> EnumerateToCheck<R, S, T, U>(IEnumerable<R> mainList, 
                                                   IEnumerable<S> secondaryList,
                                                   Func<R, T> mainListFunction, 
                                                   Func<S, T> secondaryListFunction,
                                                   Func<R, U> resultSelector,
                                                   bool ifFound)
{
    foreach (var r in mainList)
    {
        bool found = false;
        foreach (var s in secondaryList)
        {
            if (object.Equals(mainListFunction(r), secondaryListFunction(s)))
            {
                found = true;
                break;
            }
        }

        if (found == ifFound)
            yield return resultSelector(r);
    }

    //or may be just
    //return mainList.Where(r => secondaryList.Any(s => object.Equals(mainListFunction(r), secondaryListFunction(s))) == ifFound)
    //               .Select(r => resultSelector(r));
    //but I like the verbose way.. easier to debug..
}

public static IEnumerable<U> Intersect<R, S, T, U>(this IEnumerable<R> mainList, 
                                                   IEnumerable<S> toIntersectList,
                                                   Func<R, T> mainListFunction,
                                                   Func<S, T> toIntersectListFunction,
                                                   Func<R, U> resultSelector)
{
    return EnumerateToCheck(mainList, toIntersectList, mainListFunction, 
                            toIntersectListFunction, resultSelector, true);
}

现在,您可以执行以下操作:

Now in your case you can do just:

var excepts = SourceFiles.Except(DestFiles, p => p.Name, p => p.Name, p => p.FullName)
                         .ToList();

这篇关于LINQ遇到问题时,请在L​​ist&lt; FileInfo&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆