在LINQ中过滤时建立一个外部列表[英] Building an external list while filtering in LINQ

本文是小编为大家收集整理的关于在LINQ中过滤时建立一个外部列表的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我有一个输入字符串数组,其中包含域\帐户形式的电子邮件地址或帐户名称.我想构建一个仅包含电子邮件地址的字符串列表.如果输入数组中的元素的格式为 domain\account,我将在字典中进行查找.如果在字典中找到键,则该值就是电子邮件地址.如果未找到,则不会添加到结果列表中.下面的代码将使上面的描述清楚:

private bool where(string input, Dictionary<string, string> dict)
{
    if (input.Contains("@"))
    {                
        return true;
    }
    else
    {
       try
       {
           string value = dict[input];             
           return true;
       }
       catch (KeyNotFoundException)
       {
           return false;
       }
    }
}

private string select(string input, Dictionary<string, string> dict)
{
    if (input.Contains("@"))
    {                
        return input;
    }
    else
    {
        try
        {
            string value = dict[input];                    
            return value;
        }
        catch (KeyNotFoundException)
        {
            return null;
        }
    }
}
public void run()
{
    Dictionary<string, string> dict = new Dictionary<string, string>()
    {
        { "gmail\\nameless", "nameless@gmail.com"}
    };    

    string[] s = { "anonymous@gmail.com", "gmail\\nameless", "gmail\\unknown" };
    var q = s.Where(p => where(p, dict)).Select(p => select(p, dict));
    List<string> resultList = q.ToList<string>();
}

虽然上面的代码有效(希望我这里没有任何错字),但我不喜欢上面的两个问题:

  1. where() 和 select() 中的代码似乎是多余的/重复的.
  2. 需要 2 次通过.第二遍将查询表达式转换为 List.

所以我想直接在 where() 方法中添加到 List resultList 中.看来我应该可以这样做.代码如下:

private bool where(string input, Dictionary<string, string> dict, List<string> resultList)
{
    if (input.Contains("@"))
    {                
        resultList.Add(input);  //note the difference from above
        return true;
    }
    else
    {
       try
       {
           string value = dict[input];
           resultList.Add(value); //note the difference from above             
           return true;
       }
       catch (KeyNotFoundException)
       {
           return false;
       }
    }
}

我的 LINQ 表达式可以很好地在 1 条语句中:

List<string> resultList = new List<string>();
s.Where(p => where(p, dict, resultList));

或者

var q = s.Where(p => where(p, dict, resultList)); //do nothing with q afterward

这似乎是完美且合法的 C# LINQ.结果:有时有效,有时无效.那么为什么我的代码不能可靠地工作,我怎样才能让它可靠地工作呢?

推荐答案

如果你把where和select颠倒你可以先把未知的域账户转为null,然后过滤掉就行了.

private string select(string input, Dictionary<string, string> dict)
{
    if (input.Contains("@"))
    {                
        return input;
    }
    else
    {
        if (dict.ContainsKey(input))
            return dict[input];
    }
    return null;
}

var resultList = s
    .Select(p => select(p, dict))
    .Where(p => p != null)
    .ToList()

这会处理您的重复代码.

<块引用>

需要 2 次通过.第二遍从查询表达式转换为 List.

实际上这只是一次通过,因为 LINQ 是惰性求值的.这就是为什么你最后的陈述有时只起作用的原因.只有在评估 LINQ 查询时才会应用过滤器并生成您的列表.否则,永远不会运行 Where 语句.

本文地址:https://www.itbaoku.cn/post/1557049.html

问题描述

I have an array of input strings that contains either email addresses or account names in the form of domain\account. I would like to build a List of string that contains only email addresses. If an element in the input array is of the form domain\account, I will perform a lookup in the dictionary. If the key is found in the dictionary, that value is the email address. If not found, that won't get added to the result list. The code below will makes the above description clear:

private bool where(string input, Dictionary<string, string> dict)
{
    if (input.Contains("@"))
    {                
        return true;
    }
    else
    {
       try
       {
           string value = dict[input];             
           return true;
       }
       catch (KeyNotFoundException)
       {
           return false;
       }
    }
}

private string select(string input, Dictionary<string, string> dict)
{
    if (input.Contains("@"))
    {                
        return input;
    }
    else
    {
        try
        {
            string value = dict[input];                    
            return value;
        }
        catch (KeyNotFoundException)
        {
            return null;
        }
    }
}
public void run()
{
    Dictionary<string, string> dict = new Dictionary<string, string>()
    {
        { "gmail\\nameless", "nameless@gmail.com"}
    };    

    string[] s = { "anonymous@gmail.com", "gmail\\nameless", "gmail\\unknown" };
    var q = s.Where(p => where(p, dict)).Select(p => select(p, dict));
    List<string> resultList = q.ToList<string>();
}

While the above code works (hope I don't have any typo here), there are 2 problems that I do not like with the above:

  1. The code in where() and select() seems to be redundant/repeating.
  2. It takes 2 passes. The second pass converts from the query expression to List.

So I would like to add to the List resultList directly in the where() method. It seems like I should be able to do so. Here's the code:

private bool where(string input, Dictionary<string, string> dict, List<string> resultList)
{
    if (input.Contains("@"))
    {                
        resultList.Add(input);  //note the difference from above
        return true;
    }
    else
    {
       try
       {
           string value = dict[input];
           resultList.Add(value); //note the difference from above             
           return true;
       }
       catch (KeyNotFoundException)
       {
           return false;
       }
    }
}

The my LINQ expression can be nicely in 1 single statement:

List<string> resultList = new List<string>();
s.Where(p => where(p, dict, resultList));

Or

var q = s.Where(p => where(p, dict, resultList)); //do nothing with q afterward

Which seems like perfect and legal C# LINQ. The result: sometime it works and sometime it doesn't. So why doesn't my code work reliably and how can I make it do so?

推荐答案

If you reverse the where and the select you can convert unknown domain accounts to null first, then just filter them out.

private string select(string input, Dictionary<string, string> dict)
{
    if (input.Contains("@"))
    {                
        return input;
    }
    else
    {
        if (dict.ContainsKey(input))
            return dict[input];
    }
    return null;
}

var resultList = s
    .Select(p => select(p, dict))
    .Where(p => p != null)
    .ToList()

This takes care of your duplicate code.

It takes 2 passes. The second pass converts from the query expression to List.

Actually this is only one pass as LINQ is lazy evaluated. This is why your last statements only work sometimes. The filter is only applied and your list generated if the LINQ query is evaluated. Otherwise the Where statement is never run.