向订阅者发送最近的帖子电子邮件。我们应该将帖子或订阅者作为外循环进行迭代吗?

问题描述 投票:0回答:2

在我的应用程序中有帖子订阅者和帖子:

public class PostSubscriber {
    public List<int> Topics { get; set; }
    ...
}

public class Post {
    public int Id { get; set; }
    ...
}

有一个每天运行一次的预定作业,它获取最近的帖子(通常少于 30)并将它们发送给帖子订阅者。

如果订阅者有空/空

Topics
列表,那么他会收到所有最近的帖子通知。 如果订阅者有非空的
Topics
列表,那么他只会收到匹配的帖子通知。

在我当前的设置中,外循环在帖子集合上完成,订阅者在每次迭代时被过滤:

foreach(var post in posts) {
    var postSubscribers = preFetchedSubscribers.Where(i =>
        i.Topics == null ||
        !i.Topics.Any() ||
        i.Topics.Contains(post.Id)
    );
    ...
}

但我正在考虑将电子邮件连接成一封,向订阅者发送一封信,提及所有/匹配最近的帖子,而不是每个帖子发送 N 封信。

会在外循环中迭代订阅者

foreach(var subscriber in subscribers) {
    foreach(var post in posts) {
        // check if post is applicable and concatenate email
    }    
    ...
    // send concatenated email
}

效率更高/更低,为什么?

c# algorithm performance
2个回答
1
投票

如果你想将电子邮件连接成一个,除了让订阅者成为外循环之外,没有什么好办法。否则,您需要为所有订阅者存储部分创建的电子邮件,直到您完成所有帖子。


0
投票

这取决于帖子数、订阅者数和订阅的帖子数。如果它足够大,你应该考虑从订阅的帖子到订阅者引入字典(如果我计算正确,当前的实现是

O(p * s * t)
其中 p - 帖子数,s - 订阅者数量,t - 每个订阅者的平均主题数) .沿着这些线的东西:

var  preFetchedSubscribers = new[]{new {Topics = new []{1}}};
var dictionary = preFetchedSubscribers
    .SelectMany(s => s.Topics.Select(tId => (tId, s)))
    .GroupBy(t => t.tId)
    .ToDictionary(gr => gr.Key, gr => gr.Select(t => t.s).ToList());
foreach (var post in posts)
{
    if (dictionary.TryGetValue(post.Id, out var subs))
    {
        // ...
    }
}

或者使用

HashSet
代替
List

public class PostSubscriber 
{
    public HashSet<int> Topics { get; set; } = new HashSet<int>(); // so i.Topics == null not needed
    ...
}

但一如既往-跑马并以具体案例为基准。

© www.soinside.com 2019 - 2024. All rights reserved.