1

I'm calculating the decimal netscore multiple times during an application session and I'd like to compare the variable each time it changes in order to identify the three largest and three smallest values.

I'm currently storing the three largest & three smallest values in the session state. The following code doesn't always work correctly - for instance, if I set netscore to the following values in this order: 57,64,27,56,45,53,62,42,64,40,53,71,57,54,50 it will return 71,71,64 as the three largest. I expect to see 71,64,64.

Can someone identify what I'm doing wrong?

Session["top1"] = "0";
Session["top2"] = "0";
Session["top3"] = "0";
Session["bottom1"] = "100";
Session["bottom2"] = "100";
Session["bottom3"] = "100";

//Fetch values required to calculate netscore 
SqlCommand fivecmd = new SqlCommand(query5, mySLTConnection);            
var fives = Convert.ToSingle(fivecmd.ExecuteScalar());
SqlCommand fourcmd = new SqlCommand(query4, mySLTConnection);
var fours = Convert.ToSingle(fourcmd.ExecuteScalar());
SqlCommand threecmd = new SqlCommand(query3, mySLTConnection);
var threes = Convert.ToSingle(fourcmd.ExecuteScalar());
SqlCommand twocmd = new SqlCommand(query2, mySLTConnection);
var twos = Convert.ToSingle(twocmd.ExecuteScalar());
SqlCommand onecmd = new SqlCommand(query1, mySLTConnection);
var ones = Convert.ToSingle(onecmd.ExecuteScalar());
mySLTConnection.Close();

//Get total count
var total = fives + fours + threes + twos + ones;

//Get net score
var netscore = Convert.ToDecimal((((fives + fours) - (twos + ones)) / total)*100); 
netscore = Math.Round(netscore,0);


//Begin comparing netscore to stored top/bottom values
if (netscore > Convert.ToDecimal(Session["top1"]))
{
    Session["top3"] = Session["top2"];
    Session["top2"] = Session["top1"];
    Session["top1"] = netscore;
    Session["top1q"] = question.ToUpper();

}
else if (netscore > Convert.ToDecimal(Session["top2"]))
{
    Session["top3"] = Session["top2"];
    Session["top2"] = netscore;
    Session["top2q"] = question.ToUpper();
}
else if (netscore > Convert.ToDecimal(Session["top3"]))
{
    Session["top3"] = netscore;
    Session["top3q"] = question.ToUpper();
}
else if (netscore < Convert.ToDecimal(Session["bottom1"]))
{
    Session["bottom3"] = Session["bottom2"];
    Session["bottom2"] = Session["bottom1"];
    Session["bottom1"] = netscore;
    Session["bottom1q"] = question.ToUpper();
}

else if (netscore < Convert.ToDecimal(Session["bottom2"]))
{
    Session["bottom3"] = Session["bottom2"];
    Session["bottom2"] = netscore;
    Session["bottom2q"] = question.ToUpper();
}

else if (netscore < Convert.ToDecimal(Session["bottom3"]))
{
    Session["bottom3"] = netscore;
    Session["bottom3q"] = question.ToUpper();
}

lblSVal1.Text = Session["top1"].ToString();
lblSVal2.Text = Session["top2"].ToString();
lblSVal3.Text = Session["top3"].ToString();
66Mhz
  • 225
  • 2
  • 4
  • 12
  • Do you need to store the data in a session? – ohmusama May 12 '15 at 22:05
  • strange, i run you code, in console, and it say top1, 2 and 3 are 71, 64, 64. See ... http://rextester.com/SNWQIK3633 – George May 12 '15 at 22:08
  • I do not have store in session, only did it in case I needed to share across pages, which I do not. – 66Mhz May 12 '15 at 22:15
  • can you show us a little how you are getting netscore/question as it might be easier to write a closed form solution if we understand how you are sourcing your data. – ohmusama May 12 '15 at 22:20
  • Sure, added the code used to fetch and calculate `netscore`. `question` is a string that is passed to the method which contains all the included code. – 66Mhz May 12 '15 at 22:32
  • I'm confused though, how you are getting more than 1 netscore, that generally what I'm looking for, if this all "works" without a page refresh that is. – ohmusama May 12 '15 at 22:38
  • This code is called over and over again inside a method. Every time it is run, a new question is passed and a new netscore is calculated then compared. – 66Mhz May 12 '15 at 22:47
  • 1
    Okay, I'm going to provide an answer that will have some blanks for you to fill in, but it should be pretty simple. – ohmusama May 12 '15 at 22:48
  • 1
    not sure if this is relevant, but in the code you pasted `threecmd` is not called - `fourcmd` called twice instead. This got copy-pasted into the @ohnusama answer too. – anikiforov May 12 '15 at 23:41
  • Wow, totally missed that! Thanks! – 66Mhz May 13 '15 at 00:23

2 Answers2

1

A quick - but not very elegant - fix is using this pattern:

if (netscore >= Convert.ToDecimal(Session["top1"]))
{
   if (netscore > Convert.ToDecimal(Session["top1"])) {
      Session["top3"] = Session["top2"];
      Session["top2"] = Session["top1"];
      Session["top1"] = netscore;
      Session["top1q"] = question.ToUpper();  
   }
   // If it's not a new top value, it will be ignored
} 
// ... and so on ...
JimiLoe
  • 950
  • 2
  • 14
  • 22
1

This SO question pretty much answers your question, but I noticed you are also storing some related question data. I first then recommend making a little bit of a class to store the related data together.

class NetScore {
    public decimal NetScore;
    public string Message;
}

Then here is a method that would generate a netscore (add arguments so it can build the queries correctly). Also since you didn't detail how the message was loaded, I assume you can figure that out.

public NetScore GetNetScore() {
    //Fetch values required to calculate netscore 
    SqlCommand fivecmd = new SqlCommand(query5, mySLTConnection);            
    var fives = Convert.ToSingle(fivecmd.ExecuteScalar());
    SqlCommand fourcmd = new SqlCommand(query4, mySLTConnection);
    var fours = Convert.ToSingle(fourcmd.ExecuteScalar());
    SqlCommand threecmd = new SqlCommand(query3, mySLTConnection);
    var threes = Convert.ToSingle(fourcmd.ExecuteScalar());
    SqlCommand twocmd = new SqlCommand(query2, mySLTConnection);
    var twos = Convert.ToSingle(twocmd.ExecuteScalar());
    SqlCommand onecmd = new SqlCommand(query1, mySLTConnection);
    var ones = Convert.ToSingle(onecmd.ExecuteScalar());
    mySLTConnection.Close();

    //Get total count
    var total = fives + fours + threes + twos + ones;

    //Get net score
    return new NetScore() {
        NetScore = Math.Round(Convert.ToDecimal((((fives + fours) - (twos + ones)) / total)*100), 0),
        Message = //however you get the message...
    }
}

For your main code, you can load them all into a list. Since you didn't detail how you get all the net scores, I just put a foreach for whatever loop you need to do that. This basically will call the method above to generate your message/netscore objects into a list. Once it's in a list its easy to get the top3 and bottom3 with linq.

List<NetScore> netScores = new List<NetScore>();

// load all the net scores and their messages
foreach(... in ...) {
    netScore.Add(GetNetScore());
}

// get the top3 and bottom3
IEnumerable<NetScore> top3 = netScores.OrderByDescending(s => s.NetScore).Take(3);
IEnumerable<NetScore> bottom3 = netScores.OrderBy(s => s.NetScore).Take(3);

Then you can use the enumerable to write the values out to your labels like this.

lblSVal1.Txt = top3.ElementAt(0).NetScore.ToString();
lblSVal2.Txt = top3.ElementAt(1).NetScore.ToString();
lblSVal3.Txt = top3.ElementAt(2).NetScore.ToString();
Community
  • 1
  • 1
ohmusama
  • 4,159
  • 5
  • 24
  • 44
  • Thanks so much for your reply. Everything looks good, but I'm getting the following error: `Error 306 Inconsistent accessibility: return type 'NetScore' is less accessible than method 'GetNetScore(string, string, string)'` Did I place the `NetScore` class in the wrong place? – 66Mhz May 12 '15 at 23:32
  • Was able to resolve by setting the NetScore class to public: `public class NetScore` – 66Mhz May 12 '15 at 23:39
  • This solution works perfectly, results are returning as expected. Thank you again for the detailed, well-thought-out answer. You've been a huge help! – 66Mhz May 12 '15 at 23:49