Note that there are some explanatory texts on larger screens.

plurals
  1. POOptimizing an algorithm and/or structure in C#
    primarykey
    data
    text
    <p>I'm working on an application where you're able to subscribe to a newsletter and choose which categories you want to subscribe to. There's two different sets of categories: cities and categories.</p> <p>Upon sending emails (which is a scheduled tast), I have to look at which cities and which categories a subscriber has subscribed to, before sending the email. I.e., if I have subscribed to "London" and "Manchester" as my cities of choice and have chosen "Food", "Cloth" and "Electronics" as my categories, I will only get the newsletters that relates to these.</p> <p>The structure is as follows:</p> <p>On every newsitem in Umbraco CMS there is a commaseparated string of cities and categories (effectively, these are stored as node ids since cities and categories are nodes in Umbraco aswell) When I subscribe to one or more city and one or more category, I store the city and category nodeids in the database in custom tables. My relational mapping looks like this:</p> <p><img src="https://i.stack.imgur.com/fzU97.png" alt="enter image description here"></p> <p>And the whole structure looks like this:</p> <p><img src="https://i.stack.imgur.com/HELhQ.png" alt="enter image description here"></p> <p>To me, this seems like two sets of 1 - 1..* relations (one subscriber to one or many cities and one subscriber to one or many categories)</p> <p>To find which emails to send who which subscriber, my code looks like this:</p> <pre><code>private bool shouldBeAdded = false; // Dictionary containing the subscribers e-mail address and a list of news nodes which should be sent Dictionary&lt;string, List&lt;Node&gt;&gt; result = new Dictionary&lt;string, List&lt;Node&gt;&gt;(); foreach(var subscriber in subscribers) { // List of Umbraco CMS nodes to store which nodes the html should come from List&lt;Node&gt; nodesToSend = new List&lt;Node&gt; nodesToSend(); // Loop through the news foreach(var newsItem in news) { // The news item has a comma-separated string of related cities foreach (string cityNodeId in newsItem.GetProperty("cities").Value.Split(',')) { // Check if the subscriber has subscribed to the city if(subscriber.CityNodeIds.Contains(Convert.ToInt32(cityNodeId))) { shouldBeAdded = true; } } // The news item has a comma-separated string of related categories foreach (string categoryNodeId in newsItem.GetProperty("categories").Value.Split(',')) { // Check if the subscriber has subscribed to the category if(subscriber.CategoryNodeIds.Contains(Convert.ToInt32(categoryNodeId))) { shouldBeAdded = true; } } } // Store in list if (shouldBeAdded) { nodesToSend.Add(newsItem); } // Add it to the dictionary if (nodesToSend.Count &gt; 0) { result.Add(subscriber.Email, nodesToSend); } } // Ensure that we process the request only if there are any subscribers to send mails to if (result.Count &gt; 0) { foreach (var res in result) { // Finally, create/merge the markup for the newsletter and send it as an email. } } </code></pre> <p>While this works, I'm a bit concerned about performance when a certain amount of subscribers is reached since we're into three nested foreach loops. Also, remembering my old teachers preaches: "for every for loop there is a better structure" </p> <p>So, I would like your oppinion on the above solution, is there anything that can be improved here with the given structure? And will it cause performance problems over time?</p> <p>Any help/hint is greatly appreciated! :-)</p> <p>Thanks in advance.</p> <p><strong>Solution</strong></p> <p>So after a few good hours of debugging and fumblin' around I finally came up with something that works (initially, it looked like my original code worked, but it didn't)</p> <p>Sadly, I couldn't get it to work with any LINQ queries I tried, so I went back to the "ol' school' way of iterating ;-) The final algorithm looks like this:</p> <pre><code>private bool shouldBeAdded = false; // Dictionary containing the subscribers e-mail address and a list of news nodes which should be sent Dictionary&lt;string, List&lt;Node&gt;&gt; result = new Dictionary&lt;string, List&lt;Node&gt;&gt;(); foreach(var subscriber in subscribers) { // List of Umbraco CMS nodes to store which nodes the html should come from List&lt;Node&gt; nodesToSend = new List&lt;Node&gt; nodesToSend(); // Loop through the news foreach(var newsItem in news) { foreach (string cityNodeId in newsItem.GetProperty("cities").Value.Split(',')) { // Check if the subscriber has subscribed to the city if (subscriber.CityNodeIds.Contains(Convert.ToInt32(cityNodeId))) { // If a city matches, we have a base case nodesToSend.Add(newsItem); } } foreach (string categoryNodeId in newsItem.GetProperty("categories").Value.Split(',')) { // Check if the subscriber has subscribed to the category if (subscriber.CategoryNodeIds.Contains(Convert.ToInt32(categoryNodeId))) { shouldBeAdded = true; // News item matched and will be sent. Stop the loop. break; } else { shouldBeAdded = false; } } if (!shouldBeAdded) { // The news item did not match both a city and a category and should not be sent nodesToSend.Remove(newsItem); } } if (nodesToSend.Count &gt; 0) { result.Add(subscriber.Email, nodesToSend); } } // Ensure that we process the request only if there are any subscribers to send mails to if (result.Count &gt; 0) { foreach (var res in result) { // StringBuilder to build markup for newsletter StringBuilder sb = new StringBuilder(); // Build markup foreach (var newsItem in res.Value) { // build the markup here } // Email logic here } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload