sam
sam

Reputation: 427

Find a value between a List's upper and lower limits

I'm currently calculating tax rate by the following way

int testValue = 180000;

List<TaxBracket> taxes = new List<TaxBracket>();
taxes.Add(new TaxBracket(18199, 0)); //0 to 18199
taxes.Add(new TaxBracket(36999, 19)); //18200 to 36999
taxes.Add(new TaxBracket(79999, Convert.ToDecimal(32.50D))); //37000 to 79999
taxes.Add(new TaxBracket(179999, 37)); //80000 to 179999 
taxes.Add(new TaxBracket(180000, 47)); //180000

decimal result = taxes[taxes.Count-1].Value;

for (int i = 0; i < taxes.Count; i++)
{
    if (i == 0)
    {
        if (testValue < taxes[i].Limit)
            result = 0;
    }

    if (i > 0)
    {
        if (testValue < taxes[i].Limit && testValue > taxes[i - 1].Limit) 
        {
            result = taxes[i].Value;
        }
    }
}

Console.WriteLine(result);

Is there any alternate in LINQ to do this instead of using this old-school approach? Looks pretty close to this but a bit different.

Upvotes: 2

Views: 472

Answers (2)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

As a Linq alternative I suggest using binary search, providing that taxes is sorted:

int index = taxes.BinarySearch(
  new TaxBracket(testValue, -1), // -1: this parameter is ignored
  Comparer<TaxBracket>.Create((left, right) => left.Limit.CompareTo(right.Limit)));

if (index < 0)
  index = ~index - 1; // in between of two indexes

var result = taxes[index].Value;

Binary search can be useful when you work with long list (the algorithm ensures O(log(N)) complexity, when Linq provides O(N) one); but when taxes is small, Linq SkipWhile (see Gilad Green's answer) is more readable

Upvotes: 3

Gilad Green
Gilad Green

Reputation: 37299

If I understood correctly this is what you want:

var result = taxes.OrderBy(item => item.Limit)
                  .SkipWhile(item => item.Limit < testValue)
                  .FirstOrDefault()?.Value;

It will order your collection from smallest Limit to largest and then will Skip until it reaches a Limit that is equal or greater than your testValue. Because collection is ordered then the FirstOrDefault and it's value.

Or:

var result = taxes.Where(item => item.Limit >= testValue )
                  .OrderBy(item => item.Limit)
                  .FirstOrDefault()?.Value;

This way looks at the problem from a different direction. Take only the items that are equal or to testValue and then order them ant take the first.

Upvotes: 4

Related Questions