0

I have a string like string x = "AB001-AB050, AB055, AB060-AB099".

I am looking for a solution to get all the values in this range.

The output should contain all values between AB001 and AB050, then AB055 and then all values between AB060 and AB099.

Additional details: The 2 first char are only letters. The 3 last char are only numbers.

Example : given AB008 - AB012, AB020 Expected o/p : AB008, AB009, AB010,AB011, AB012,AB020

  • What's your input data? – Maciej Los Sep 09 '20 at 08:37
  • Is the format of the identifiers always `` and you want all the identifiers with the same letters, but the digits varying between the lower and upper number? – Lasse V. Karlsen Sep 09 '20 at 08:40
  • And is it always going to be the same letters in the lower and upper range? Or could you have this? `AB001-AC050` ? – Lasse V. Karlsen Sep 09 '20 at 08:41
  • 1
    Consider AB001 like a base 36 number. Then the incrementation from AB001 to AB002 can be done by converting to base 10. https://stackoverflow.com/questions/923771/quickest-way-to-convert-a-base-10-number-to-any-base-in-net. I assume that you will be able to split on `,` and `-` to get the range bound. If not that 3 questions. – Drag and Drop Sep 09 '20 at 08:52
  • Here's a rather naive example: https://dotnetfiddle.net/hJZH1W – Lasse V. Karlsen Sep 09 '20 at 09:07
  • Yes go question is it a base 26 (increment the letter)? Or a number with a prefix ? – Drag and Drop Sep 09 '20 at 09:15
  • When writing a question always go back to [ask] and [mre]. Here you were missing many thing. The 2 first char are only letters? Y/N. The 3 last char are only numbers? Y/N. You ask for all value betwwen range but somehow you manage to give no information about the expected result. In your test input, select edge case. Reduce range size to "enought element to show a behavior". Be clear and specific. For "009">"010", that's just standard numbers, you can achieve that with int.Parse and int.ToString. – Drag and Drop Sep 09 '20 at 13:26

2 Answers2

1

are you looking for something like this?

var prefix = "AB";
var x = "AB001-AB050, AB055, AB060-AB099";

var lean_x = x.Replace(prefix, "");

var ranges = lean_x.Split(',').Select(x =>
{
    var  interval = x.Split('-').Select(x => Convert.ToInt32(x)).ToArray();
    if (interval.Count() > 1)
        return Enumerable.Range(interval[0], interval[1] - interval[0] + 1);
        
    return Enumerable.Range(interval[0], 1);
}).SelectMany(p => p, (p,q) =>String.Format("{0}{1:000}", prefix, q));


ranges.Dump();
Derviş Kayımbaşıoğlu
  • 28,492
  • 4
  • 50
  • 72
  • Nice, if the prefix has to be increment. Remove the `Replace`. Wrap every `interval[]` with a `FromBase26To10`. And the `SelectMany` with `FromBase10ToBase26`. – Drag and Drop Sep 09 '20 at 09:18
  • good hint. @SagarChopra the rest is yours. put some effort on it :) – Derviş Kayımbaşıoğlu Sep 09 '20 at 09:24
  • I have submitted an answers because I found fun to solve this variant with only copy past. May I used your solution for the parsing? I can do simple for loop easy. The answer is below. – Drag and Drop Sep 09 '20 at 09:55
  • @Lasse V. Karlsen . The Format will always be [A-Z][A-Z][0-9][0-9][0-9]. – Sagar Chopra Sep 09 '20 at 09:58
  • @DervişKayımbaşıoğlu This was exactly what I was trying to achieve, I want to increment the prefix though. This is really helpful. Thank You. – Sagar Chopra Sep 09 '20 at 10:01
  • @DragandDrop Thank you for your elaborated answer. One issue with this is, it gives values correctly from AB001- AB009 then it starts giving AB00A,AB00B and so on and after some values it starts from AB010. Ideally , the expected behaviour is to show AB010 after AB009. – Sagar Chopra Sep 09 '20 at 13:13
0

If you need no increment the Letter too here is a solution base on the link and the existing answer.

Base 26 -> 10 convertion :
Based on Quickest way to convert a base 10 number to any base in .NET?

private static readonly char[] BaseChars =
 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
private static readonly Dictionary<char, int> CharValues = BaseChars
           .Select((c, i) => new { Char = c, Index = i })
           .ToDictionary(c => c.Char, c => c.Index);

public static string LongToBase(long value)
{
    long targetBase = BaseChars.Length;
    // Determine exact number of characters to use.
    char[] buffer = new char[Math.Max(
               (int)Math.Ceiling(Math.Log(value + 1, targetBase)), 1)];

    var i = buffer.Length;
    do
    {
        buffer[--i] = BaseChars[value % targetBase];
        value = value / targetBase;
    }
    while (value > 0);

    return new string(buffer, i, buffer.Length - i);
}

public static long BaseToLong(string number)
{
    char[] chrs = number.ToCharArray();
    int m = chrs.Length - 1;
    int n = BaseChars.Length, x;
    long result = 0;
    for (int i = 0; i < chrs.Length; i++)
    {
        x = CharValues[chrs[i]];
        result += x * (long)Math.Pow(n, m--);
    }
    return result;
}

Parsing the input :
Based on Dervis answer

var ranges = valuePart.Split(',')
                      .Select(x => {
                          var range = x.Split('-')
                                        .Select(x=> x.Trim()) // only thing I typed
                                        .ToList();

                          if (range.Count() > 1)
                              return RangeLong(BaseToLong(range[0]), BaseToLong(range[1]) - BaseToLong(range[0]) + 1);

                          return RangeLong(BaseToLong(range[0]), 1);
                      });

var result = ranges.SelectMany(x => x.Select(y=> LongToBase(y))).ToList();
        

Simple implementation of Enumerable range for long : Based on https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,1271

static IEnumerable<long> RangeLong(long start, long count)
{
    for (long i = 0; i < count; i++) yield return start + i;
}
    

Live demo.

This code was build only using copypast from code and ressource linked on this very question, only .Select(x=> x.Trim()) was typed

Drag and Drop
  • 2,672
  • 3
  • 25
  • 37