views:

203

answers:

7

Hi - I have a string with a number at the end, after a dash ("-"). I'd like to create that same string with that number incremented by 1. Pretty simple, but I'm wondering if there's a better approach to this? Thanks!

string oldString = "BA-0001-3";
int lastIndex = oldString.LastIndexOf("-");
string oldNumber = oldString.SubString(lastIndex + 1);
string oldPartialString = oldString.SubString(0, lastIndex);
int newNumber = Convert.ToInt32(oldNumber) + 1;

string newString = oldPartialString + newNumber.ToString();
+5  A: 

Regex?

Example:

Regex.Replace("BA-0001-3", @"[A-Z]{2}-\d{4}-(\d+)", 
    m => (Convert.ToInt32(m.Groups[1].Value) + 1).ToString())
gandjustas
Not very helpful without an example.
Michael Petrotta
Can't ask someone to learn things on their own?
Ed Swangren
@Ed Swangren: from the current answer, sure. From the previous version, just a blurted reference - that's not good enough for SO.
Michael Petrotta
+1  A: 

Honestly, I think this approach is right.

Jonathan
+5  A: 

I would probably use my friend string.Split:

string oldString = "BA-0001-3";
string[] parts = oldString.Split('-');
parts[parts.Length-1] = (Convert.ToInt32(parts[parts.Length-1])+1).ToString();
string newString = string.Join("-", parts);

A small tweak that will perhaps make it quicker (by accessing parts.Length and subtracting 1 only once - didn't profile so it's purely a guess, and it is likely a marginal difference anyway), but above all more robust (by using int.TryParse):

string oldString = "BA-0001-3";
string[] parts = oldString.Split('-');
int number;
int lastIndex = parts.Length-1;
parts[lastIndex] = (int.TryParse(parts[lastIndex], out number) ? ++number : 1).ToString();
string newString = string.Join("-", parts);
Fredrik Mörk
I considered using .Split, but it just seemed a bit overpowered for this. Any thoughts on pros/cons of this approach?
morganpdx
@morganpdx: Don't know if it's really a pro, but I like that it clearly decomposes the string into its "building blocks" and let you manipulate the pieces separately. This is highly subjective of course, but it my eyes it feels more precise than using `SubString` with offsets.
Fredrik Mörk
I'd first go for `Split()` too. No messing around with char indexes in strings... With indexes it is easy to get off-by-one errors. If you need to do more than 1 000 000 a second, you can consider optimizing it.
jdv
True, I can see the attraction :) And adding the int.TryParse definitely ups the cool factor...
morganpdx
@jdv - yeah I hate those off by one errors.
morganpdx
I like this solution, however after testing it doesn't increment the number. Any idea why?
morganpdx
@morganpdx: yes, there was a bug in my code, my fault. Instead of `number++` it should be `++number`. I have updated the answer.
Fredrik Mörk
+1  A: 

Here's an example of how it could be done with RegEx:

public void Test()
{
    System.Text.RegularExpressions.Regex rx = new Regex(@"(?<prefix>.*\-)(?<digit>\d+)");
    string input = "BA-0001-3";
    string output = string.Empty;

    int digit = 0;
    if (int.TryParse(rx.Replace(input, "${digit}"), out digit))
    {
        output = rx.Replace(input, "${prefix}" + (digit + 1));
    }
    Console.WriteLine(output);
}
Steven
Nice! regex AND int.TryParse. This looks pretty robust...
morganpdx
+2  A: 

Updated per Ahmad Mageed's comments below. This is his answer much more than it is mine now :-)

I would do it the way you have it now, but for fun wanted to see if I could do it with linq.

var x = "BA-0001-3".Split('-');

var y = x.First() + "-" + x.ElementAt(1) + "-" + (Convert.ToInt32(x.Last()) + 1);

This works in LINQPad.

Edit: Obviously I'm not a pro with linq. Hopefully there will be other answers/comments on how this can be improved.

adrift
This works but can be tidied up a bit. There's no need for the `AsEnumerable()` call or the entire first LINQ query. Your code works just as well if you write: `var x = "BA-0001-3".Split('-');` Also you can use `First()`, `ElementAt(N)`, and `Last()` to avoid the `Take`/`Skip`/`Single` calls. That makes it a lot more compact, especially since we're working with a small amount of elements.
Ahmad Mageed
@Ahmad - Nice, thank you!
adrift
@adrift - very cool. I never would have thought to use LINQ. But then, i'm a complete linq newbie.
morganpdx
@adrift no prob. +1 for the effort!
Ahmad Mageed
A: 

Here is an example of a class that exposes the three parts of your "part number". Not particularly fancy (also note the absence of error checking/validation).

  class Program
  {
    static void Main(string[] args)
    {
      PartNumber p1 = new PartNumber("BA-0001-3");
      for (int i = 0; i < 5; i++)
      {
        p1.Sub++;
        Debug.WriteLine(p1);
      }

      PartNumber p2 = new PartNumber("BA", 2, 3);
      for (int i = 0; i < 5; i++)
      {
        p2.Sub++;
        Debug.WriteLine(p2);
      }

    }
  }

  class PartNumber
  {
    public PartNumber(string make, int model, int sub)
    {
      Make = make;
      Model = model;
      Sub = sub;
    }

    public PartNumber(string part)
    {
      //Might want to validate the string here
      string [] fields = part.Split('-');

      //Are there 3 fields?  Are second and third fields valid ints?
      Make = fields[0];
      Model = Int32.Parse(fields[1]);
      Sub = Int32.Parse(fields[2]);
    }

    public string Make { get; set; }
    public int Model { get; set; }
    public int Sub { get; set; }

    public override string ToString()
    {
      return string.Format("{0}-{1:D4}-{2}", Make, Model, Sub);
    }
  }
wageoghe
+1  A: 

Using the regex (which already seems to have now been filled in with more details) I end up with something like:

var regex = new Regex(@"^(?<Category>[A-Za-z]{1,2})-(?<Code>[0-9]{4})-(?<Number>[0-9]+)$");
var newCode = regex.Replace("BA-0001-3", new MatchEvaluator(ReplaceWithIncrementedNumber));

Where the MatchEvaluator function is:

public static string ReplaceWithIncrementedNumber(Match match)
{
    Debug.Assert(match.Success);
    var number = Int32.Parse(match.Groups["Number"].Value);
    return String.Format("{0}-{1}-{2}", match.Groups["Category"].Value, match.Groups["Code"].Value, number + 1);
}
Reddog