Reputation: 3009
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
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
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
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
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
Reputation: 1575
Just round up
double a = 1.1;
if(d < 0.0)
return -1;
else
return (int)Math.Ceiling(a);
Upvotes: 1