Most efficient way to loop through two C# Lists
I have a method which I call CalculatePopularityScore
. It exists on a Story
object. The Story
object has a field which is an ICollection of Comment objects.
public virtual ICollection<Comment> Comments { get; set; }
The Comment
object has another collection of Reply
objects.
My method looks at the story, loops through the comments associated with that story, and if the story's comments has replies, adds up that total. That, along with some other fields, gives me a very (and I stress this) very rudimentary algorithm of a story's popularity.
public double CalculateStoryPopularityScore()
{
if (Comments == null) throw new ArgumentException("Comments can't be null");
if (Comments.Count < 0) throw new ArgumentException("Comments can't be less than zero.");
int ReplyCountSum = 0;
double ReplyScore;
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double viewCount;
foreach (var comment in Comments)
{
int replyCount;
if (comment.Replies == null)
{
throw new ArgumentNullException("Replies cannot be null");
}
if (comment.Replies.Count() == 0)
{
replyCount = 0;
} else
{
replyCount = comment.Replies.Count();
}
ReplyCountSum += replyCount;
}
ReplyScore = ReplyCountSum * 4;
CommentScore = Comments.Count() * 4;
InsightfulVoteScore = InsightFulVoteCount * 3;
UsefulVoteScore = UsefulVoteCount * 2;
viewCount = ViewCount;
double PopularityScore = CommentScore + ReplyScore + InsightfulVoteScore + + UsefulVoteScore + viewCount;
return PopularityScore;
}
This seems to work fine. Now, what I'd like to do is take this method and apply it to a number of stories (i.e. a list).
I currently have this method written. It has not yet implemented another loop to look through the replies to the comments collection of a story. I know nested loops are considered bad and slow. What would be the most efficient way to look at the list of stories, then the list of comments in each story, add up those replies, and calculate a story's popularity score?
public void CalculateStoryPopularityScore(List<Story> stories)
{
if (stories == null) throw new ArgumentException("Stories can't be null");
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double PopularityScore;
double ViewCount;
foreach (var story in stories)
{
CommentScore = story.Comments.Count() * 4;
InsightfulVoteScore = story.InsightFulVoteCount * 3;
UsefulVoteScore = story.UsefulVoteCount * 2;
ViewCount = story.ViewCount;
PopularityScore = CommentScore + InsightfulVoteScore + UsefulVoteScore + ViewCount;
story.PopularityScore = PopularityScore;
}
}
c#
|
show 3 more comments
I have a method which I call CalculatePopularityScore
. It exists on a Story
object. The Story
object has a field which is an ICollection of Comment objects.
public virtual ICollection<Comment> Comments { get; set; }
The Comment
object has another collection of Reply
objects.
My method looks at the story, loops through the comments associated with that story, and if the story's comments has replies, adds up that total. That, along with some other fields, gives me a very (and I stress this) very rudimentary algorithm of a story's popularity.
public double CalculateStoryPopularityScore()
{
if (Comments == null) throw new ArgumentException("Comments can't be null");
if (Comments.Count < 0) throw new ArgumentException("Comments can't be less than zero.");
int ReplyCountSum = 0;
double ReplyScore;
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double viewCount;
foreach (var comment in Comments)
{
int replyCount;
if (comment.Replies == null)
{
throw new ArgumentNullException("Replies cannot be null");
}
if (comment.Replies.Count() == 0)
{
replyCount = 0;
} else
{
replyCount = comment.Replies.Count();
}
ReplyCountSum += replyCount;
}
ReplyScore = ReplyCountSum * 4;
CommentScore = Comments.Count() * 4;
InsightfulVoteScore = InsightFulVoteCount * 3;
UsefulVoteScore = UsefulVoteCount * 2;
viewCount = ViewCount;
double PopularityScore = CommentScore + ReplyScore + InsightfulVoteScore + + UsefulVoteScore + viewCount;
return PopularityScore;
}
This seems to work fine. Now, what I'd like to do is take this method and apply it to a number of stories (i.e. a list).
I currently have this method written. It has not yet implemented another loop to look through the replies to the comments collection of a story. I know nested loops are considered bad and slow. What would be the most efficient way to look at the list of stories, then the list of comments in each story, add up those replies, and calculate a story's popularity score?
public void CalculateStoryPopularityScore(List<Story> stories)
{
if (stories == null) throw new ArgumentException("Stories can't be null");
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double PopularityScore;
double ViewCount;
foreach (var story in stories)
{
CommentScore = story.Comments.Count() * 4;
InsightfulVoteScore = story.InsightFulVoteCount * 3;
UsefulVoteScore = story.UsefulVoteCount * 2;
ViewCount = story.ViewCount;
PopularityScore = CommentScore + InsightfulVoteScore + UsefulVoteScore + ViewCount;
story.PopularityScore = PopularityScore;
}
}
c#
This sounds like something you should be doing in the DB/DB query, not in the code. A "currentValue" that is updated via a Add/Update Trigger on the Comments table ora view can both be used to cache such data. Doing this operation every time the page is draw or a postback of this page appears is just a massive waste of resources.
– Christopher
Nov 14 '18 at 22:51
You can also consider using Dictionaries to help avoid inner loops.
– Drew Kennedy
Nov 14 '18 at 22:52
I know nested loops are considered bad and slow
have you measured? is it really slow for your purposes? How big are your data? And how often this calculation needs to happen?
– trailmax
Nov 14 '18 at 22:54
Note that I am normally very firmly about "not putting process data into the DB". But something like "StoryPopularityScore" based on comments (often querried, very rarely changed) just screams for some for of caching.
– Christopher
Nov 14 '18 at 22:55
1
By the way, it doesn't make any sense to throw anArgumentException
from a method that doesn't take any arguments (in the first example). You should probably re-write it so that thecomments
are passed in as a parameter. Also, local variables should becamelCase
, notPascalCase
in c#. :)
– Rufus L
Nov 15 '18 at 0:03
|
show 3 more comments
I have a method which I call CalculatePopularityScore
. It exists on a Story
object. The Story
object has a field which is an ICollection of Comment objects.
public virtual ICollection<Comment> Comments { get; set; }
The Comment
object has another collection of Reply
objects.
My method looks at the story, loops through the comments associated with that story, and if the story's comments has replies, adds up that total. That, along with some other fields, gives me a very (and I stress this) very rudimentary algorithm of a story's popularity.
public double CalculateStoryPopularityScore()
{
if (Comments == null) throw new ArgumentException("Comments can't be null");
if (Comments.Count < 0) throw new ArgumentException("Comments can't be less than zero.");
int ReplyCountSum = 0;
double ReplyScore;
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double viewCount;
foreach (var comment in Comments)
{
int replyCount;
if (comment.Replies == null)
{
throw new ArgumentNullException("Replies cannot be null");
}
if (comment.Replies.Count() == 0)
{
replyCount = 0;
} else
{
replyCount = comment.Replies.Count();
}
ReplyCountSum += replyCount;
}
ReplyScore = ReplyCountSum * 4;
CommentScore = Comments.Count() * 4;
InsightfulVoteScore = InsightFulVoteCount * 3;
UsefulVoteScore = UsefulVoteCount * 2;
viewCount = ViewCount;
double PopularityScore = CommentScore + ReplyScore + InsightfulVoteScore + + UsefulVoteScore + viewCount;
return PopularityScore;
}
This seems to work fine. Now, what I'd like to do is take this method and apply it to a number of stories (i.e. a list).
I currently have this method written. It has not yet implemented another loop to look through the replies to the comments collection of a story. I know nested loops are considered bad and slow. What would be the most efficient way to look at the list of stories, then the list of comments in each story, add up those replies, and calculate a story's popularity score?
public void CalculateStoryPopularityScore(List<Story> stories)
{
if (stories == null) throw new ArgumentException("Stories can't be null");
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double PopularityScore;
double ViewCount;
foreach (var story in stories)
{
CommentScore = story.Comments.Count() * 4;
InsightfulVoteScore = story.InsightFulVoteCount * 3;
UsefulVoteScore = story.UsefulVoteCount * 2;
ViewCount = story.ViewCount;
PopularityScore = CommentScore + InsightfulVoteScore + UsefulVoteScore + ViewCount;
story.PopularityScore = PopularityScore;
}
}
c#
I have a method which I call CalculatePopularityScore
. It exists on a Story
object. The Story
object has a field which is an ICollection of Comment objects.
public virtual ICollection<Comment> Comments { get; set; }
The Comment
object has another collection of Reply
objects.
My method looks at the story, loops through the comments associated with that story, and if the story's comments has replies, adds up that total. That, along with some other fields, gives me a very (and I stress this) very rudimentary algorithm of a story's popularity.
public double CalculateStoryPopularityScore()
{
if (Comments == null) throw new ArgumentException("Comments can't be null");
if (Comments.Count < 0) throw new ArgumentException("Comments can't be less than zero.");
int ReplyCountSum = 0;
double ReplyScore;
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double viewCount;
foreach (var comment in Comments)
{
int replyCount;
if (comment.Replies == null)
{
throw new ArgumentNullException("Replies cannot be null");
}
if (comment.Replies.Count() == 0)
{
replyCount = 0;
} else
{
replyCount = comment.Replies.Count();
}
ReplyCountSum += replyCount;
}
ReplyScore = ReplyCountSum * 4;
CommentScore = Comments.Count() * 4;
InsightfulVoteScore = InsightFulVoteCount * 3;
UsefulVoteScore = UsefulVoteCount * 2;
viewCount = ViewCount;
double PopularityScore = CommentScore + ReplyScore + InsightfulVoteScore + + UsefulVoteScore + viewCount;
return PopularityScore;
}
This seems to work fine. Now, what I'd like to do is take this method and apply it to a number of stories (i.e. a list).
I currently have this method written. It has not yet implemented another loop to look through the replies to the comments collection of a story. I know nested loops are considered bad and slow. What would be the most efficient way to look at the list of stories, then the list of comments in each story, add up those replies, and calculate a story's popularity score?
public void CalculateStoryPopularityScore(List<Story> stories)
{
if (stories == null) throw new ArgumentException("Stories can't be null");
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double PopularityScore;
double ViewCount;
foreach (var story in stories)
{
CommentScore = story.Comments.Count() * 4;
InsightfulVoteScore = story.InsightFulVoteCount * 3;
UsefulVoteScore = story.UsefulVoteCount * 2;
ViewCount = story.ViewCount;
PopularityScore = CommentScore + InsightfulVoteScore + UsefulVoteScore + ViewCount;
story.PopularityScore = PopularityScore;
}
}
c#
c#
edited Nov 14 '18 at 23:17
Erik Philips
41.1k691124
41.1k691124
asked Nov 14 '18 at 22:47
J.G.SableJ.G.Sable
254213
254213
This sounds like something you should be doing in the DB/DB query, not in the code. A "currentValue" that is updated via a Add/Update Trigger on the Comments table ora view can both be used to cache such data. Doing this operation every time the page is draw or a postback of this page appears is just a massive waste of resources.
– Christopher
Nov 14 '18 at 22:51
You can also consider using Dictionaries to help avoid inner loops.
– Drew Kennedy
Nov 14 '18 at 22:52
I know nested loops are considered bad and slow
have you measured? is it really slow for your purposes? How big are your data? And how often this calculation needs to happen?
– trailmax
Nov 14 '18 at 22:54
Note that I am normally very firmly about "not putting process data into the DB". But something like "StoryPopularityScore" based on comments (often querried, very rarely changed) just screams for some for of caching.
– Christopher
Nov 14 '18 at 22:55
1
By the way, it doesn't make any sense to throw anArgumentException
from a method that doesn't take any arguments (in the first example). You should probably re-write it so that thecomments
are passed in as a parameter. Also, local variables should becamelCase
, notPascalCase
in c#. :)
– Rufus L
Nov 15 '18 at 0:03
|
show 3 more comments
This sounds like something you should be doing in the DB/DB query, not in the code. A "currentValue" that is updated via a Add/Update Trigger on the Comments table ora view can both be used to cache such data. Doing this operation every time the page is draw or a postback of this page appears is just a massive waste of resources.
– Christopher
Nov 14 '18 at 22:51
You can also consider using Dictionaries to help avoid inner loops.
– Drew Kennedy
Nov 14 '18 at 22:52
I know nested loops are considered bad and slow
have you measured? is it really slow for your purposes? How big are your data? And how often this calculation needs to happen?
– trailmax
Nov 14 '18 at 22:54
Note that I am normally very firmly about "not putting process data into the DB". But something like "StoryPopularityScore" based on comments (often querried, very rarely changed) just screams for some for of caching.
– Christopher
Nov 14 '18 at 22:55
1
By the way, it doesn't make any sense to throw anArgumentException
from a method that doesn't take any arguments (in the first example). You should probably re-write it so that thecomments
are passed in as a parameter. Also, local variables should becamelCase
, notPascalCase
in c#. :)
– Rufus L
Nov 15 '18 at 0:03
This sounds like something you should be doing in the DB/DB query, not in the code. A "currentValue" that is updated via a Add/Update Trigger on the Comments table ora view can both be used to cache such data. Doing this operation every time the page is draw or a postback of this page appears is just a massive waste of resources.
– Christopher
Nov 14 '18 at 22:51
This sounds like something you should be doing in the DB/DB query, not in the code. A "currentValue" that is updated via a Add/Update Trigger on the Comments table ora view can both be used to cache such data. Doing this operation every time the page is draw or a postback of this page appears is just a massive waste of resources.
– Christopher
Nov 14 '18 at 22:51
You can also consider using Dictionaries to help avoid inner loops.
– Drew Kennedy
Nov 14 '18 at 22:52
You can also consider using Dictionaries to help avoid inner loops.
– Drew Kennedy
Nov 14 '18 at 22:52
I know nested loops are considered bad and slow
have you measured? is it really slow for your purposes? How big are your data? And how often this calculation needs to happen?– trailmax
Nov 14 '18 at 22:54
I know nested loops are considered bad and slow
have you measured? is it really slow for your purposes? How big are your data? And how often this calculation needs to happen?– trailmax
Nov 14 '18 at 22:54
Note that I am normally very firmly about "not putting process data into the DB". But something like "StoryPopularityScore" based on comments (often querried, very rarely changed) just screams for some for of caching.
– Christopher
Nov 14 '18 at 22:55
Note that I am normally very firmly about "not putting process data into the DB". But something like "StoryPopularityScore" based on comments (often querried, very rarely changed) just screams for some for of caching.
– Christopher
Nov 14 '18 at 22:55
1
1
By the way, it doesn't make any sense to throw an
ArgumentException
from a method that doesn't take any arguments (in the first example). You should probably re-write it so that the comments
are passed in as a parameter. Also, local variables should be camelCase
, not PascalCase
in c#. :)– Rufus L
Nov 15 '18 at 0:03
By the way, it doesn't make any sense to throw an
ArgumentException
from a method that doesn't take any arguments (in the first example). You should probably re-write it so that the comments
are passed in as a parameter. Also, local variables should be camelCase
, not PascalCase
in c#. :)– Rufus L
Nov 15 '18 at 0:03
|
show 3 more comments
2 Answers
2
active
oldest
votes
Use SelectMany
var commentCount = story.Comments.Count();
// count all replies to all comments for a story
var replyCountSum = story.Comments
.SelectMany(c => c.Replies)
.Count();
Apply to a collection of stories:
stories.Select(s => new
{
Story = s,
CommentCount = s.Comments.Count(),
ReplyCount = s.Comments.SelectMany(c => c.Replies).Count(),
});
add a comment |
Unless I'm missing something, all the scores you're calculating with a separate method can instead be written as a public read-only (calculated) property of the Story
class. The reply count can be obtained by using SelectMany
(which is used to flatten lists of lists into a single list) and then getting the Count
property:
public class Story
{
public List<Comment> Comments { get; set; }
public int InsightFulVoteCount { get; set; }
public int UsefulVoteCount { get; set; }
public int ViewCount { get; set; }
public int PopularityScore
{
get
{
return
(Comments?.Count ?? 0) * 4 +
(Comments?.SelectMany(comment => comment.Replies).Count() ?? 0) * 4 +
InsightFulVoteCount * 3 +
UsefulVoteCount * 2 +
ViewCount;
}
}
}
public class Comment
{
public List<string> Replies { get; set; }
}
In case you're not familiar with the null-conditional operator (?.
), it returns null
if the left operand (the object) is null before accessing the right operand (property or method of the object). If the left side is not null, then the property/method value is returned.
Then the null-coalescing operator (??
) evaluates the left operand (which is the result of the property or method access) and, if it's null, it returns the right operand ('0'
in our case).
Basically this simplifies the code. You don't have to do:
var score = 0;
if (Comments != null) score = Comments.Count;
You can just do:
var score = Comments?.Count ?? 0;
Thanks. I'm less concerned with the method which revolves around a single story than the method which accepts a list of stories and has to rank them.
– J.G.Sable
Nov 15 '18 at 1:01
Well after the above you can just dovar rankedStories = stories.OrderByDescending(story => story.PopularityScore);
– Rufus L
Nov 15 '18 at 2:07
OrderByDescending
puts the highest rank first. Technically you should be able to use your existing method, too...I just think a property is more readable. To use your existing method you would just dovar ranked = stories.OrderByDescending(s => s.CalculateStoryPopularityScore());
, but you'd want to get rid of the exception throwing part and just return zero instead.
– Rufus L
Nov 15 '18 at 3:43
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53309872%2fmost-efficient-way-to-loop-through-two-c-sharp-lists%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Use SelectMany
var commentCount = story.Comments.Count();
// count all replies to all comments for a story
var replyCountSum = story.Comments
.SelectMany(c => c.Replies)
.Count();
Apply to a collection of stories:
stories.Select(s => new
{
Story = s,
CommentCount = s.Comments.Count(),
ReplyCount = s.Comments.SelectMany(c => c.Replies).Count(),
});
add a comment |
Use SelectMany
var commentCount = story.Comments.Count();
// count all replies to all comments for a story
var replyCountSum = story.Comments
.SelectMany(c => c.Replies)
.Count();
Apply to a collection of stories:
stories.Select(s => new
{
Story = s,
CommentCount = s.Comments.Count(),
ReplyCount = s.Comments.SelectMany(c => c.Replies).Count(),
});
add a comment |
Use SelectMany
var commentCount = story.Comments.Count();
// count all replies to all comments for a story
var replyCountSum = story.Comments
.SelectMany(c => c.Replies)
.Count();
Apply to a collection of stories:
stories.Select(s => new
{
Story = s,
CommentCount = s.Comments.Count(),
ReplyCount = s.Comments.SelectMany(c => c.Replies).Count(),
});
Use SelectMany
var commentCount = story.Comments.Count();
// count all replies to all comments for a story
var replyCountSum = story.Comments
.SelectMany(c => c.Replies)
.Count();
Apply to a collection of stories:
stories.Select(s => new
{
Story = s,
CommentCount = s.Comments.Count(),
ReplyCount = s.Comments.SelectMany(c => c.Replies).Count(),
});
edited Nov 14 '18 at 23:18
answered Nov 14 '18 at 23:12
MohoMoho
11.1k11723
11.1k11723
add a comment |
add a comment |
Unless I'm missing something, all the scores you're calculating with a separate method can instead be written as a public read-only (calculated) property of the Story
class. The reply count can be obtained by using SelectMany
(which is used to flatten lists of lists into a single list) and then getting the Count
property:
public class Story
{
public List<Comment> Comments { get; set; }
public int InsightFulVoteCount { get; set; }
public int UsefulVoteCount { get; set; }
public int ViewCount { get; set; }
public int PopularityScore
{
get
{
return
(Comments?.Count ?? 0) * 4 +
(Comments?.SelectMany(comment => comment.Replies).Count() ?? 0) * 4 +
InsightFulVoteCount * 3 +
UsefulVoteCount * 2 +
ViewCount;
}
}
}
public class Comment
{
public List<string> Replies { get; set; }
}
In case you're not familiar with the null-conditional operator (?.
), it returns null
if the left operand (the object) is null before accessing the right operand (property or method of the object). If the left side is not null, then the property/method value is returned.
Then the null-coalescing operator (??
) evaluates the left operand (which is the result of the property or method access) and, if it's null, it returns the right operand ('0'
in our case).
Basically this simplifies the code. You don't have to do:
var score = 0;
if (Comments != null) score = Comments.Count;
You can just do:
var score = Comments?.Count ?? 0;
Thanks. I'm less concerned with the method which revolves around a single story than the method which accepts a list of stories and has to rank them.
– J.G.Sable
Nov 15 '18 at 1:01
Well after the above you can just dovar rankedStories = stories.OrderByDescending(story => story.PopularityScore);
– Rufus L
Nov 15 '18 at 2:07
OrderByDescending
puts the highest rank first. Technically you should be able to use your existing method, too...I just think a property is more readable. To use your existing method you would just dovar ranked = stories.OrderByDescending(s => s.CalculateStoryPopularityScore());
, but you'd want to get rid of the exception throwing part and just return zero instead.
– Rufus L
Nov 15 '18 at 3:43
add a comment |
Unless I'm missing something, all the scores you're calculating with a separate method can instead be written as a public read-only (calculated) property of the Story
class. The reply count can be obtained by using SelectMany
(which is used to flatten lists of lists into a single list) and then getting the Count
property:
public class Story
{
public List<Comment> Comments { get; set; }
public int InsightFulVoteCount { get; set; }
public int UsefulVoteCount { get; set; }
public int ViewCount { get; set; }
public int PopularityScore
{
get
{
return
(Comments?.Count ?? 0) * 4 +
(Comments?.SelectMany(comment => comment.Replies).Count() ?? 0) * 4 +
InsightFulVoteCount * 3 +
UsefulVoteCount * 2 +
ViewCount;
}
}
}
public class Comment
{
public List<string> Replies { get; set; }
}
In case you're not familiar with the null-conditional operator (?.
), it returns null
if the left operand (the object) is null before accessing the right operand (property or method of the object). If the left side is not null, then the property/method value is returned.
Then the null-coalescing operator (??
) evaluates the left operand (which is the result of the property or method access) and, if it's null, it returns the right operand ('0'
in our case).
Basically this simplifies the code. You don't have to do:
var score = 0;
if (Comments != null) score = Comments.Count;
You can just do:
var score = Comments?.Count ?? 0;
Thanks. I'm less concerned with the method which revolves around a single story than the method which accepts a list of stories and has to rank them.
– J.G.Sable
Nov 15 '18 at 1:01
Well after the above you can just dovar rankedStories = stories.OrderByDescending(story => story.PopularityScore);
– Rufus L
Nov 15 '18 at 2:07
OrderByDescending
puts the highest rank first. Technically you should be able to use your existing method, too...I just think a property is more readable. To use your existing method you would just dovar ranked = stories.OrderByDescending(s => s.CalculateStoryPopularityScore());
, but you'd want to get rid of the exception throwing part and just return zero instead.
– Rufus L
Nov 15 '18 at 3:43
add a comment |
Unless I'm missing something, all the scores you're calculating with a separate method can instead be written as a public read-only (calculated) property of the Story
class. The reply count can be obtained by using SelectMany
(which is used to flatten lists of lists into a single list) and then getting the Count
property:
public class Story
{
public List<Comment> Comments { get; set; }
public int InsightFulVoteCount { get; set; }
public int UsefulVoteCount { get; set; }
public int ViewCount { get; set; }
public int PopularityScore
{
get
{
return
(Comments?.Count ?? 0) * 4 +
(Comments?.SelectMany(comment => comment.Replies).Count() ?? 0) * 4 +
InsightFulVoteCount * 3 +
UsefulVoteCount * 2 +
ViewCount;
}
}
}
public class Comment
{
public List<string> Replies { get; set; }
}
In case you're not familiar with the null-conditional operator (?.
), it returns null
if the left operand (the object) is null before accessing the right operand (property or method of the object). If the left side is not null, then the property/method value is returned.
Then the null-coalescing operator (??
) evaluates the left operand (which is the result of the property or method access) and, if it's null, it returns the right operand ('0'
in our case).
Basically this simplifies the code. You don't have to do:
var score = 0;
if (Comments != null) score = Comments.Count;
You can just do:
var score = Comments?.Count ?? 0;
Unless I'm missing something, all the scores you're calculating with a separate method can instead be written as a public read-only (calculated) property of the Story
class. The reply count can be obtained by using SelectMany
(which is used to flatten lists of lists into a single list) and then getting the Count
property:
public class Story
{
public List<Comment> Comments { get; set; }
public int InsightFulVoteCount { get; set; }
public int UsefulVoteCount { get; set; }
public int ViewCount { get; set; }
public int PopularityScore
{
get
{
return
(Comments?.Count ?? 0) * 4 +
(Comments?.SelectMany(comment => comment.Replies).Count() ?? 0) * 4 +
InsightFulVoteCount * 3 +
UsefulVoteCount * 2 +
ViewCount;
}
}
}
public class Comment
{
public List<string> Replies { get; set; }
}
In case you're not familiar with the null-conditional operator (?.
), it returns null
if the left operand (the object) is null before accessing the right operand (property or method of the object). If the left side is not null, then the property/method value is returned.
Then the null-coalescing operator (??
) evaluates the left operand (which is the result of the property or method access) and, if it's null, it returns the right operand ('0'
in our case).
Basically this simplifies the code. You don't have to do:
var score = 0;
if (Comments != null) score = Comments.Count;
You can just do:
var score = Comments?.Count ?? 0;
edited Nov 15 '18 at 3:48
answered Nov 15 '18 at 0:40
Rufus LRufus L
18.6k31631
18.6k31631
Thanks. I'm less concerned with the method which revolves around a single story than the method which accepts a list of stories and has to rank them.
– J.G.Sable
Nov 15 '18 at 1:01
Well after the above you can just dovar rankedStories = stories.OrderByDescending(story => story.PopularityScore);
– Rufus L
Nov 15 '18 at 2:07
OrderByDescending
puts the highest rank first. Technically you should be able to use your existing method, too...I just think a property is more readable. To use your existing method you would just dovar ranked = stories.OrderByDescending(s => s.CalculateStoryPopularityScore());
, but you'd want to get rid of the exception throwing part and just return zero instead.
– Rufus L
Nov 15 '18 at 3:43
add a comment |
Thanks. I'm less concerned with the method which revolves around a single story than the method which accepts a list of stories and has to rank them.
– J.G.Sable
Nov 15 '18 at 1:01
Well after the above you can just dovar rankedStories = stories.OrderByDescending(story => story.PopularityScore);
– Rufus L
Nov 15 '18 at 2:07
OrderByDescending
puts the highest rank first. Technically you should be able to use your existing method, too...I just think a property is more readable. To use your existing method you would just dovar ranked = stories.OrderByDescending(s => s.CalculateStoryPopularityScore());
, but you'd want to get rid of the exception throwing part and just return zero instead.
– Rufus L
Nov 15 '18 at 3:43
Thanks. I'm less concerned with the method which revolves around a single story than the method which accepts a list of stories and has to rank them.
– J.G.Sable
Nov 15 '18 at 1:01
Thanks. I'm less concerned with the method which revolves around a single story than the method which accepts a list of stories and has to rank them.
– J.G.Sable
Nov 15 '18 at 1:01
Well after the above you can just do
var rankedStories = stories.OrderByDescending(story => story.PopularityScore);
– Rufus L
Nov 15 '18 at 2:07
Well after the above you can just do
var rankedStories = stories.OrderByDescending(story => story.PopularityScore);
– Rufus L
Nov 15 '18 at 2:07
OrderByDescending
puts the highest rank first. Technically you should be able to use your existing method, too...I just think a property is more readable. To use your existing method you would just do var ranked = stories.OrderByDescending(s => s.CalculateStoryPopularityScore());
, but you'd want to get rid of the exception throwing part and just return zero instead.– Rufus L
Nov 15 '18 at 3:43
OrderByDescending
puts the highest rank first. Technically you should be able to use your existing method, too...I just think a property is more readable. To use your existing method you would just do var ranked = stories.OrderByDescending(s => s.CalculateStoryPopularityScore());
, but you'd want to get rid of the exception throwing part and just return zero instead.– Rufus L
Nov 15 '18 at 3:43
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53309872%2fmost-efficient-way-to-loop-through-two-c-sharp-lists%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
This sounds like something you should be doing in the DB/DB query, not in the code. A "currentValue" that is updated via a Add/Update Trigger on the Comments table ora view can both be used to cache such data. Doing this operation every time the page is draw or a postback of this page appears is just a massive waste of resources.
– Christopher
Nov 14 '18 at 22:51
You can also consider using Dictionaries to help avoid inner loops.
– Drew Kennedy
Nov 14 '18 at 22:52
I know nested loops are considered bad and slow
have you measured? is it really slow for your purposes? How big are your data? And how often this calculation needs to happen?– trailmax
Nov 14 '18 at 22:54
Note that I am normally very firmly about "not putting process data into the DB". But something like "StoryPopularityScore" based on comments (often querried, very rarely changed) just screams for some for of caching.
– Christopher
Nov 14 '18 at 22:55
1
By the way, it doesn't make any sense to throw an
ArgumentException
from a method that doesn't take any arguments (in the first example). You should probably re-write it so that thecomments
are passed in as a parameter. Also, local variables should becamelCase
, notPascalCase
in c#. :)– Rufus L
Nov 15 '18 at 0:03