-2

This is a running pace/time calculator I'm working on. This particular problem I'm having is finding the average pace for entered mile time values. I have the user enter multiple values in hh:mm:ss format via text boxes. I need to add the values together, find the average of the values entered, and return that average value to a label in hh:mm:ss format. I've tried various ways of doing this and read multiple other posts but keep getting stuck.

  • I've tried using List but the number of values entered always varies, I need it to be dynamic. If there's a way to only enter the user-defined values into the list, my list would work. As of now, it gets the average of all my boxes even if one of them doesn't have a value.

  • Also tried just adding the TimeSpan values together and divide them by a count. This gets the correct count for how many values were entered by the user. However, I get an error saying cannot apply the / operator to operands of type TimeSpan and int.

This is not the same scenario as Find average of collection of TimeSpans. The code used in that article broke my entire app.

        private void calculate_btn_Click(object sender, EventArgs e)
        {
            TimeSpan d1 = TimeSpan.Parse(textBox1.Text);
            TimeSpan d2 = TimeSpan.Parse(textBox2.Text);
            TimeSpan d3 = TimeSpan.Parse(textBox3.Text);

            var sourceList = new List<TimeSpan>();

            sourceList.Add(d1);
            sourceList.Add(d2);
            sourceList.Add(d3);

            var averageTimeSpan = new TimeSpan(Convert.ToInt64(sourceList.Average(timeSpan => timeSpan.Ticks)));
            averagelabletext.Text = averageTimeSpan.ToString();
        }

    private void calculate_btn_Click(object sender, EventArgs e)
    {
        TimeSpan d1 = TimeSpan.Parse(textBox1.Text);
        TimeSpan d2 = TimeSpan.Parse(textBox2.Text);
        TimeSpan d3 = TimeSpan.Parse(textBox3.Text);

        TimeSpan total = (d1 + d2 + d3);

        int count = 0;

        foreach (Control c in Controls)
        {
            if (!(c is TextBox)) continue;
            TextBox t = c as TextBox;
            {
                if (t.Text != "00:00:00")
                {
                    count++;
                }
            }
        }

        averagelabletext.Text = total / count;
    }

I expect if a user enters 00:09:00 into textbox1 and 00:09:30 into textbox 2 the result should be 00:09:15.

Right now, if a user enters those same values, the average returned is 00:06:10 since it's still calculating that third value into the average. Since the user hasn't entered a value into that box, it's still 00:00:00.

  • 2
    Calculate the average of `TimeSpan.Ticks` (which is a long, and can be divided by int). Then convert back to a TimeSpan using `TimeSpan.FromTicks`. – Joe Mar 26 '19 at 20:39
  • 2
    Use where before average, like sourceList.Where(x => x > 0).Average... – Senad Meškin Mar 26 '19 at 20:40
  • 1
    Possible duplicate of [Find average of collection of TimeSpans](https://stackoverflow.com/questions/8847679/find-average-of-collection-of-timespans) – mjwills Mar 26 '19 at 20:41
  • Put a breakpoint on `if (t.Text != "00:00:00")`. Run to it. What happens for the control where no time has been entered as yet? Does it enter the `if`. What is the value of `t.Text` at that point? – mjwills Mar 26 '19 at 20:48
  • @RobertMcKee all text boxes have a starting value of 00:00:00 already entered when the app is opened – Tanner Logan Mar 26 '19 at 20:49
  • @mjwills not really focused on that part of it. It's returning the correct value since all text boxes have an "auto-mask" value of 00:00:00. So it will always at least return 0. – Tanner Logan Mar 26 '19 at 20:52

2 Answers2

1

Filter the results that weren't entered before doing the average by using Where:

private void calculate_btn_Click(object sender, EventArgs e)
{
    var d1 = TimeSpan.Parse(textBox1.Text);
    var d2 = TimeSpan.Parse(textBox2.Text);
    var d3 = TimeSpan.Parse(textBox3.Text);

    var sourceList = new List<TimeSpan> {d1,d2,d3};

    var averageTicks = sourceList.Where(t=>t.Ticks>0).Average(t=>t.Ticks);
    var averageTimeSpan = new TimeSpan(averageTicks);
    averagelabletext.Text = averageTimeSpan.ToString();
}

I would likely do something more like this (for clarity):

private void calculate_btn_Click(object sender, EventArgs e)
{
    var textboxes = new List<TextBox> {textBox1, textBox2, textBox3};
    var ticks = textboxes.Select(t=>TimeSpan.Parse(t.Text).Ticks);
    var enteredTicks = ticks.Where(t=>t!=0);

    var averageTicks = enteredTicks.Average();
    var averageTimeSpan = new TimeSpan(averageTicks);
    averagelabletext.Text = averageTimeSpan.ToString();
}

And then simplified further:

private void calculate_btn_Click(object sender, EventArgs e)
{
    var textboxes = new List<TextBox> {textBox1, textBox2, textBox3};
    var ticks = textboxes
        .Select(t=>TimeSpan.Parse(t.Text).Ticks)
        .Where(t=>t!=0);
        .Average();

    var averageTimeSpan = new TimeSpan(ticks);
    averagelabletext.Text = averageTimeSpan.ToString();
}
Robert McKee
  • 21,305
  • 1
  • 43
  • 57
0

Try using TryParse. If it still ends up parsing the empty string, add a check for !string.IsNullOrWhiteSpace(TextBox1.Text) && TimeSpan.TryParse...

        private void calculate_btn_Click(object sender, EventArgs e)
        {
            var sourceList = new List<TimeSpan>();
            TimeSpan timeSpan;
            if (TimeSpan.TryParse(textBox1.Text, out timeSpan)) {
              sourceList.Add(timeSpan);
            }
            if (TimeSpan.TryParse(textBox2.Text, out timeSpan)) {
              sourceList.Add(timeSpan);
            }
            if (TimeSpan.TryParse(textBox3.Text, out timeSpan)) {
              sourceList.Add(timeSpan);
            }

            var averageTimeSpan = new TimeSpan(Convert.ToInt64(sourceList.Average(x => x.Ticks)));
            averagelabletext.Text = averageTimeSpan.ToString();
        }
kilkfoe
  • 445
  • 5
  • 11