【问题标题】:Linq to get list of custom objects where a certain value contained within dictionary property equals a specific valueLinq 获取自定义对象列表,其中字典属性中包含的某个值等于特定值
【发布时间】:2015-12-21 03:38:45
【问题描述】:

我正在努力解决这个问题,并且已经搜索了多种方法,但似乎找不到答案。我从其他人那里继承了这个应用程序,需要向应用程序添加一些功能。我以前没有过多地使用字典和 linq,所以我一直在搜索并尝试获取知识来做我需要做的事情。

有一个类具有以下属性(删除了一些本次讨论不需要的属性):

class EmailRecord
{
    public Dictionary<string, List<string>> emails = new Dictionary<string, List<string>>();
    public string RecordID { get; set; }

[后跟其他属性和构造函数...]

创建对象时, emails 属性的键中会包含一个模板字符串,值中包含一个包含电子邮件地址的字符串列表。出于我的目的,我不需要知道密钥中的内容。

我有一个名为 allRecords 的 EmailRecord 对象列表。我需要查询 allRecords 以获取所有 EmailRecord 对象的列表,其中电子邮件字典属性的值列表包含我存储在名为 recipientEmail 的变量中的特定电子邮件地址。密钥无关紧要,电子邮件显示多少次也无关紧要。如果电子邮件出现在 emails 属性值的任何位置,我只需要包含在结果中的对象实例。在 EmailRecord 的一个实例中,电子邮件字典属性可能有两个键,并且在每个键中,值的字符串列表中的多个电子邮件。我不需要限制特定的键,我只需要知道电子邮件是否存在于该字典中任何位置的电子邮件字符串列表中的任何位置。

我尝试了一些东西,最新的是这个(不起作用):

var results = EmailRecords
    .SelectMany(x => x.emails)
    .Where(x => x.Value.Contains(recipientEmail));

上面似乎只是返回字典属性,而不是整个对象。

我希望能够通过以下方式遍历结果:

foreach (EmailRecord foundRecord in results) {
    ...do work here
}

在我尝试学习 Linq 时有什么想法或建议可以帮助我吗?提前感谢您提供的任何帮助。

【问题讨论】:

  • 所以你想遍历整个字典中的List&lt;string&gt;,并检查它是否包含recipientEmail?如果是的话,做一些工作吗?
  • 我想获取 EmailRecord 对象的结果列表,其中特定电子邮件位于该电子邮件字典中的任何位置。然后我将遍历该结果集。这有意义吗?
  • 但我在您的示例代码中没有看到任何 List&lt;EmailRecord&gt; 变量可以循环
  • 因此,如果 EmailRecords(EmailRecord 对象列表)有 3 个 EmailRecord 对象,并说前两个对象将“test@test.com”作为对象的电子邮件字典中的值之一,第三个对象对象根本没有显示该电子邮件,那么链接查询应该只给我前两个对象的结果。这就是试图完成但无法弄清楚的事情。
  • 好的。检查我的更新答案

标签: c# linq dictionary


【解决方案1】:

如果您想循环遍历其中一个emails 属性值包含recipientEmailEmailRecord 对象,那么您首先需要有一个EmailRecord 列表。然后搜索它们。以下应该可以解决问题。

List<EmailRecord> EmailRecords = new List<EmailRecord>();

//Fill the EmailRecords somewhere

foreach (KeyValuePair<string, List<string>> emailfoundRecord in 
                  EmailRecords.emails.Where(x => x.Value.Contains(recipientEmail)))
 {
   //do work here       
 }

【讨论】:

  • 谢谢亚历克斯。当我尝试你的时,对于 EmailRecords.emails 中的 emailfoundRecord 部分,它无法识别电子邮件。当我看到 Corey 的帖子并且现在也在尝试该解决方案时,我不确定为什么并且正在排除故障。
  • 由于EmailRecordsList,您需要枚举它以获取emails 成员。最好在发布之前在 LINQPad 之类的东西中测试代码,看看它是否有效。
【解决方案2】:

当您致电 EmailRecords.SelectMany(x =&gt; x.Emails) 时,您得到的是 IEnumerable&lt;KeyValuePair&lt;string, List&lt;string&gt;&gt;&gt; 或类似名称。显然,这不是您所追求的结果,因为它剥离了所有其他信息。

对于 LINQ,在每个阶段首先要考虑的是您期望从查询中得到什么。在这种情况下,查询结果应该是 EmailRecord 实例的枚举,这也是我们输入的内容。过滤该列表最简单的是使用 Where 方法完成,所以这就是您应该完成所有工作的地方

接下来确定您的过滤条件并编写适合的过滤谓词。对于任何给定的EmailRecord,我们想查明任何字典条目是否包含特定的电子邮件地址。由于字典值是列表,我们将使用Contains 进行实际比较,并使用Any 测试字典本身。

看起来像这样:

var filtered = EmailRecords.Where(e => 
    e.Emails.Any(kv => 
        kv.Value.Contains(recipientEmail)
    )
);

这是因为字典也是可枚举的,枚举中的每个条目都是键/值对。

Any 将在找到单个匹配条目时停止使用,而不是继续到每个 EmailRecord 实例的 Emails 字典的末尾。如果有很多电子邮件并且您期望有大量选择,那么这可能会节省一些时间。但是可能不会,因为通常这种结构中没有很多重复的电子邮件地址。

取决于您希望执行此操作的频率,但是构建查找和查询可能会更快。假设您的 EmailRecords 列表不经常更改并且您正在执行大量此类查找,您可以获得很大的加速。

我将使用Dictionary&lt;string, EmailRecord[]&gt; 进行查找,因为一旦我们获得了所有电子邮件地址对和EmailRecord 对象对的列表,构建它(相当)简单:

var emailReferences = EmailRecords.SelectMany(e => 
    e.Emails.SelectMany(kv => 
        kv.Value.Select(v => 
            new { address = v, record = e }
        )
    )
);

var lookup = 
    emailReferences
    .GroupBy(i => i.address, i => i.record)
    .ToDictionary(g => g.Key, g => g.ToArray());
;

由此您将能够非常简单快速地找到一个电子邮件地址并获得其引用的EmailRecord 实例:

EmailRecord[] filtered = null;
lookup.TryGetValue(recipientEmail, out filtered);

这将比上面等效的 LINQ 更快,但是对于大型列表,设置可能会消耗大量时间和内存。如果您的列表很小或经常更改(因为查找必须重新生成或至少在每次更改时失效),那么这不会提高您的程序的速度。


顺便说一句,这是我在处理以List&lt;&gt; 为值的字典时使用的扩展方法:

public static partial class extensions
{
    public static Dictionary<TKey, List<TElem>> Add<TKey, TElem>(this Dictionary<TKey, List<TElem>> dict, TKey key, TElem value)
    {
        List<TElem> list;
        if (dict.ContainsKey(key))
            list = dict[key];
        else
            dict[key] = list = new List<TElem>();

        list.Add(value);
        return dict;
    }
}

它有助于使您的添加更简单、更易于阅读。

【讨论】:

  • Corey,谢谢你的回答,它工作得很好,完成了我所需要的!我也感谢所有背景,这对我学习很有帮助,谢谢!
  • 我认为,当看起来有人正在学习事物的工作原理时,最好提供更详细的答案。比单行“做这个”类型的答案要好:) 提供替代方案也很好,可以根据要求展示每件事如何有多种解决方法。
猜你喜欢
  • 1970-01-01
  • 2021-09-15
  • 2021-12-16
  • 2019-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多