4

Scenario: Database has table with list of account numbers. Account numbers range from 0-9999. Customer is allowed to make account numbers for customers within that range, as they see fit.

Need: I am producing a report that shows unused account numbers in range format. So, I need a list of strings, showing available account numbers, in range format.

Example: Account numbers 0, 1, 2, 4, 20, 21, 22 are all being used in data. So the result list would be...

3
5-19
23-9999

Been stumbling around on this all day. How to do this with straight-up c#?

Chase
  • 162
  • 1
  • 10
  • 2
    Are you expecting a SQL statement(s) within the C# program or is this to be done only in C# (and arrays to simulate a db)? – NoChance Oct 24 '13 at 21:01
  • Thanks everyone for the input. Much appreciated. I have tried everyone's suggestions, but it was user2700792's suggestion that not only worked, but gave me the exact results I was looking for. Thanks again. VIVA LA STACKOVERFLOW! – Chase Oct 25 '13 at 13:15

6 Answers6

7

Use Range and Except

var acctNos = new List<int>() { 0,1,2,4,20,21,22 };

var unusedAcctNos = Enumerable.Range(0,9999).ToList().Except(acctNos);

Then to group contiguous integers, modify the accepted solution given here.

Community
  • 1
  • 1
Josh
  • 8,082
  • 5
  • 43
  • 41
3

Depending on how you're getting your account data, something like:

var nums = Enumerable.Range(0, 9999).ToList();
var usedNums = new List<int> {0, 1, 2, 3, 4, 20, 21, 22};
var availableNums = nums.Except(usedNums);

That will get you an IEnumerable of the available integers. For you then to format them as "3, 5-19, 23-99" would require additional logic.

AllenG
  • 8,112
  • 29
  • 40
1
//assuming already sorted
var a = Enumerable.Range(0, 10000);
//assuming alredy sorted
var b = new List<int>(){0, 1, 2, 4, 20, 21, 22};
//get the values not used yet in sorted order
var c = a.Except(b).ToList();
//store the list range
List<string> range = new List<string>();

for(int i = 0; i < c.Count; i++)
{
    //current start range
    int current = c[i];
    string r = current.ToString();

    int next;
    if(current > b.Last())
        next = c.Last() + 1;
    else
        next = b.FirstOrDefault( x => x > current);


    if( next != current+1)
        r += "-" + (next-1).ToString();

   range.Add(r);


   while(c[i] < next-1) i++;
 }
  • Awesome, thanks you. Looks like this does exactly what I need. Much, much appreciated. I have never used Linq before, so it being a possible solution never occurred to me. Thanks. – Chase Oct 25 '13 at 13:12
0
bool[] accountNumbers = new bool[10000];

for(int accountNumber in collection){

    accountNumbers[accountNumber] = true;

}


string available = '';

for(int i = 0; i <  accountNumbers.length; ++i){

 if(!accountNumbers[i]) available += (i + ',');
}

available = available.substring(0, available.length-2);
Darius
  • 5,180
  • 5
  • 47
  • 62
0

Large solution :)

  static List<string> getAvailablesRanges(List<int> data,int maxrange)
        {
            List<string> ranges = new List<string>();
            string last = null;
            foreach (int i in data)
            {
                if (i == data.Max() && i != maxrange)
                {
                    if (last != null) ranges.Add(last + "-" + i);
                    ranges.Add(i + "-" + maxrange);
                }
                else if (last == null)
                    last = i.ToString();
                else
                {
                    if(i-int.Parse(last)>1) ranges.Add(last + "-" + i);
                    last = null;
                }
            }
            return ranges;
        }
Sam
  • 2,950
  • 1
  • 18
  • 26
  • Show your test and your result – Sam Oct 24 '13 at 21:39
  • It's wrong, you must pass the original list, not the enumerable – Sam Oct 24 '13 at 21:45
  • that gives the wrong results, in the example I get {"0-1", "2-4", "20-21", "22-98", "98-9999"} it should be {"3", "5-19", "23-97", "99-9998"} unless I'm mistaken as to what the user was expecting – Eluvatar Oct 24 '13 at 21:48
  • If i have understood what you mean, this should resolve the problem `if(i-int.Parse(last)>1)` – Sam Oct 24 '13 at 21:54
0

you'll have to test this as I just wrote it (used joshes solution to get the list)

var acctNos = new List<int>() { 0,1,2,4,20,21,22 };
var unusedAcctNos = Enumerable.Range(0, 10000).Except(acctNos).ToList();
StringBuilder builder = new StringBuilder();
int lastNo = unusedAcctNos.Last();
int previousVal = -2;
bool isRange = false;
foreach (int i in unusedAcctNos)
{
    if (i == previousVal + 1 && i != lastNo) //is in range
    {
        previousVal = i;
        isRange = true;
        continue;
    }
    else if (previousVal > -1) //range broke
    {
        if (isRange)
        {
            builder.Append("-");
            if (i == lastNo && previousVal == i - 1)
            {
                builder.Append(i);
                break;
            }
            else
            {
                builder.Append(previousVal);
            }
            isRange = false;
        }

        builder.Append(",");//change group splitter here
    }

    builder.Append(i);

    previousVal = i;
}
Eluvatar
  • 2,265
  • 19
  • 24