PhilBrown
PhilBrown

Reputation: 3009

switch style C# construct with double range cases

I'm trying to come up with the best way of doing some kind of switch style selection on a double to find out what range it is in. Something like this:

double a = 1.1;
switch(a)
{
case: (0.0, 1.6)
 return 1;
case: (1.6, 2.337)
 return 2;
case: (2.337, 3.2974)
 return 3;
default:
  return -1;
}

Obviously in this example, one value in the range would have to be non-inclusive, but you get my drift. Any Ideas?

Edit, the ranges are not necessarily integral.

EDIT 2: I'm actually dealing with radians and finding out which of 12 ranges a point is in. I ended up doing this:

double pi = Math.PI;
double[] zones = new double[] {
        0, pi/6, pi/3, pi/2,
        2*pi/3, 5*pi/6, pi, 7*pi/6,
        4*pi/3, 3*pi/2, 5*pi/3, 11*pi/6
    };

for (int i = 0; i < 11; i++)
{
    if (radians > zones[i] && radians <= zones[i + 1])
    {
        return i + 1;
    }
}

I started to do an binary search type if-else, but it was going to get too messy.

Upvotes: 4

Views: 19318

Answers (5)

Ani
Ani

Reputation: 10896

Perhaps this will do what you want to do with a nice syntactic structure. Please adjust visibilities and comparisons according to your purposes:

public static class Range
{
    public interface ISwitchable
    {
        void SetDefault(Action defaultStatement);
        void Execute();
    }

    public interface ISwitchable<T>: ISwitchable
        where T: IComparable<T>
    {
        T Value { get; set; }
        void AddCase(ICase<T> caseStatement);
    }

    public class Switchable<T> : ISwitchable<T>
        where T: IComparable<T>
    {
        private readonly IList<ICase<T>> _caseStatements = new List<ICase<T>>();
        private Action _defaultStatement;

        #region ISwitchable<T> Members

        public T Value { get; set; }

        public void AddCase(ICase<T> caseStatement)
        {
            _caseStatements.Add(caseStatement);
        }

        public void SetDefault(Action defaultStatement)
        {
            _defaultStatement = defaultStatement;
        }

        public void Execute()
        {
            foreach (var caseStatement in _caseStatements)
                if ((caseStatement.Min.CompareTo(Value) <= 0) && (caseStatement.Max.CompareTo(Value) > 0))
                {
                    caseStatement.Action();
                    return;
                }

            _defaultStatement();
        }

        #endregion
    }

    public interface ICase<T>
        where T: IComparable<T>
    {
        T Min { get; set; }
        T Max { get; set; }

        Action Action { get; set; }
    }

    public sealed class Case<T>: ICase<T>
        where T: IComparable<T>
    {
        #region ICase<T> Members

        public T Min { get; set; }

        public T Max { get; set; }

        public Action Action { get; set; }

        #endregion
    }

    public static ISwitchable<T> Switch<T>(T value)
        where T: IComparable<T>
    {
        return new Switchable<T>();
    }
}

public static class SwitchableExtensions
{
    public static Range.ISwitchable<T> Case<T>(this Range.ISwitchable<T> switchable, T min, T max, Action action)
        where T: IComparable<T>
    {
        switchable.AddCase(new Range.Case<T>{ Min = min, Max = max, Action = action });
        return switchable;
    }
    public static void Default(this Range.ISwitchable switchable, Action action)
    {
        switchable.SetDefault(action);
        switchable.Execute();
    }
}

Usage:

class Program
{
    static void Main(string[] args)
    {
        Range.Switch(10)
            .Case(0, 3, () => Console.WriteLine("Between 0 and 3"))
            .Case(3, 7, () => Console.WriteLine("Between 3 and 7"))
            .Default(() => Console.WriteLine("Something else"));
    }
}

Upvotes: 1

eouw0o83hf
eouw0o83hf

Reputation: 9598

You can't do that with a switch.

if(a >= 0 && a < 1) return 1;
else if(a < 2) return 2;

etc.

Alternatively, you could write up a class to hold minval, maxval, and outputval, then just iterate over those possibilities and return the outputval that matches.

Upvotes: 0

Magnus Hoff
Magnus Hoff

Reputation: 22089

The following is ideally suited to adjacent ranges, since you only need to write the range limits once:

double a = 1.1;

if (a < 0.0) {
    // Too low
    return -1;
} else if (a < 1.6) {
    // Range [0.0, 1.6)
    return 1;
} else if (a < 2.337) {
    // Range [1.6, 2.337)
    return 2;
} else if (a < 3.2974) {
    // Range [2.337, 3.2974)
    return 3;
} else {
    // Too high
    return -1;
}

Upvotes: 8

dcarneiro
dcarneiro

Reputation: 7170

This is probably over engineering but you could create a Range Class with Min and Max values and a Callback Function.

Then just create all the Ranges with respective min, and max values and callback and add them to an IEnumerable.

Use LINQ to find out the correct range:

range = Ranges.First(r => r.MinValue <= value and r.MaxValue > value);

Then just call the range callback.

Upvotes: 3

Terkel
Terkel

Reputation: 1575

Just round up

double a = 1.1;
if(d < 0.0)
  return -1;
else
  return (int)Math.Ceiling(a);

Upvotes: 1

Related Questions