繁体   English   中英

Linq获取自定义对象的列表,其中字典属性中包含的某个值等于一个特定值

[英]Linq to get list of custom objects where a certain value contained within dictionary property equals a specific value

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

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

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

[其次是其他属性和构造函数...]

创建对象时,电子邮件属性将在键中包含模板字符串,并在值中包含包含电子邮件地址的字符串列表。 就我的目的而言,我不需要知道密钥中的内容。

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

我已经尝试了一些方法,最近的方法是这样的(不起作用):

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

上面的代码似乎只是在返回dictionary属性,而不是整个对象。

我希望能够像这样遍历结果:

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

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

如果要遍历EmailRecord对象,该对象的emails属性之一包含recipientEmail ,则首先需要有一个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.SelectMany(x => x.Emails) ,您得到的是IEnumerable<KeyValuePair<string, List<string>>>或类似名称。 显然,这不是您追求的结果,因为它剥夺了所有其他信息。

使用LINQ,在每个阶段要考虑的第一件事是您期望从查询中获得什么。 在这种情况下,查询结果应该是EmailRecord实例的枚举,这也是我们要输入的内容。最简单的过滤列表是使用Where方法,因此您应该在其中进行所有工作

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

看起来像这样:

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

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

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

取决于您要执行此操作的频率,但是构建查询和查询可能更快。 假设您的EmailRecords列表不经常更改,并且您正在进行大量此类查找,则可以大大提高速度。

我将使用Dictionary<string, EmailRecord[]>进行查找,因为一旦我们获得了所有成对的电子邮件地址和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<>为值的字典时使用的扩展方法:

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;
    }
}

它有助于使您的添加内容更简单易读。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM