如何避免嵌套循环解决以下问题

时间:2020-03-12 17:55:19

标签: c# linq

public class job
    {
        public int Id { get; set; }
        public string JobNumber { get; set; }
        public bool IsValid { get; set; }
    }  

 public class attachment
    {
        public string JobNumber { get; set; }
        public int id { get; set; }

    }

        List<job> joblist = new List<job>();
        List<attachment> attachments = new List<attachment>();

        for (int i = 0; i < joblist.Count; i++)
        {
            for (int j = 0; j < attachments.Count; j++)
            {
                if(joblist[i].JobNumber == attachments[j].JobNumber)
                {
                    joblist[i].IsValid = true;
                }
            }
        }

如果作业列表类和附件列表类都具有相同的作业号,我们将其设为有效 有什么办法可以使用linq来改进此代码

3 个答案:

答案 0 :(得分:2)

使用Any查找具有相应JobNumber的附件:

foreach (var j in joblist)
{
    j.IsValid = attachments.Any(a => a.JobNumber == j.JobNumber);
}

请记住,这仍然是(最坏的情况) O(n²)操作,尽管平均而言它的效率比原始代码要高一些,因为查找后循环没有break一场比赛。

答案 1 :(得分:1)

使用HashSet,可以优化附件为O(1)的测试,但是创建HashSet的开销只有在列表很长的情况下才值得附件或很长的工作列表,或者将相同的附件列表与许多不同的工作列表一起使用:

var HasAttachment = attachments.Select(a => a.JobNumber).ToHashSet();

for (int i = 0; i < joblist.Count; i++) {
    joblist[i].IsValid = HasAttachment.Contains(joblist[i].JobNumber);
}

答案 2 :(得分:0)

这不是纯粹的LINQ,但是您可以对内部循环的joblist.Count枚举进行交易,并为attachments的一个枚举完全删除它。

HashSet<string> attachmentJobNumbers = new HashSet<string>(
    // A one-time enumeration of attachments to collect its JobNumber values
    attachments.Select(attachment => attachment.JobNumber)
);

for (int i = 0; i < joblist.Count; i++)
{
    joblist[i].IsValid = attachmentJobNumbers.Contains(joblist[i].JobNumber);
}

HashSet<>允许快速查找特定JobNumber是否存在任何附件。假定没有等效的JobNumber值仅因大小写而有所不同。否则,您可以将StringComparer传递给HashSet<>构造函数的重载...

HashSet<string> attachmentJobNumbers = new HashSet<string>(
    // A one-time enumeration of attachments to collect its JobNumber values
    attachments.Select(attachment => attachment.JobNumber),
    StringComparer.OrdinalIgnoreCase
);

...获得不区分大小写的查询。

(从@NetMage's answer中我了解到ToHashSet() extension method自.NET 4.7.2 / .NET Core 2.0以来就存在了,因此这是一种方便的选择。)

使用ToDictionary() ...做纯净的LINQ方式做同样的事情...

// A one-time enumeration of attachments to collect its JobNumber values
Dictionary<string, attachment> attachmentsByJobNumber = attachments.ToDictionary(attachment => attachment.JobNumber);

for (int i = 0; i < joblist.Count; i++)
{
    joblist[i].IsValid = attachmentsByJobNumber.ContainsKey(joblist[i].JobNumber);
}

...或ToLookup(),尽管在不需要attachment的情况下,也可以通过JobNumber来获得attachment,而只需{{1 }}对“ bool是否有附件?”的答案。我认为JobNumber可以更好地代表此用例。

此外,请记住,.NET的naming guidelines要求typesproperties使用Pascal大小写,因此您应该使用HashSet<>Job,和Attachment(或者可能是Id)。