views:

243

answers:

8

How can I refactor this so that numberOfItems doesn't have to be declared as a variable?

//method: gets the text in a string in front of a marker, if marker is not there, then return empty string
//example: GetTextAfterMarker("documents/jan/letter043.doc","/") returns "letter043.doc"
//example: GetTextAfterMarker("letter043.doc","/") returns ""
//example: GetTextAfterMarker("letter043.doc",".") returns "doc"
public static string GetTextAfterMarker(string line, string marker)
{
    int numberOfItems = line.Split(new string[] { marker }, StringSplitOptions.None).Count();

    string result = line.Split(new string[] { marker }, StringSplitOptions.None)[numberOfItems-1];
    return line.Equals(result) ? string.Empty : result;
}
+4  A: 

If you're using 3.5 this is trivial with Linq:

public static string GetTextAfterMarker2(string line, string marker)
{
  string result = line.Split(new string[] { marker }, StringSplitOptions.None).Last();
  return line.Equals(result) ? string.Empty : result;
}
Badaro
exactly what I was looking for, thanks.
Edward Tanguay
To be honest, I think my final answer is still cleaner than this. But it was slower.
Matthew Scharley
+1 This is a better answer and faithfully reproduces what the OP originally posted.
Andrew Hare
Why do it with Split()? LastIndexOf() is faster and doesn't allocate memory.
Anton Tykhyy
@Matthew - I meant that this was a better answer than my answer, not yours :)
Andrew Hare
@Anton Laziness, I took the code from the question and only made the minimum adjustments necessary. :)
Badaro
The question is ambiguous too... is the code using Split and that's the end of it, or does he actually want to refactor it so it still uses Split? In the latter case we're both wrong Anton :(
Matthew Scharley
There is the functional spec in the comment, according to that we're correct :)
Anton Tykhyy
It's no contest. The LastIndexOf solution is far, far superior to anything using Split.
Stephen Martin
A: 
public static string GetTextAfterMarker(string line, string marker)
{
    string result = line.Split(new string[] { marker }, StringSplitOptions.None)[line.Split(new string[] { marker }, StringSplitOptions.None).Count()-1];
    return line.Equals(result) ? string.Empty : result;
}

Why you'd want to do that though is beyond me... It's messy, to say the least.

Perhaps:

public static string GetTextAfterMarker(string line, string marker)
{
    string[] tmp = line.Split(new string[] { marker }, StringSplitOptions.None);
    string result = tmp[tmp.Length-1];
    return line.Equals(result) ? string.Empty : result;
}

Or you might even consider using .IndexOf and .Substring instead, something like this (untested):

public static string GetTextAfterMarker(string line, string marker)
{
    int index = line.LastIndexOf(marker);
    return index < 0 ? string.Empty : line.Substring(index + marker.Length);
}
Matthew Scharley
Need to change tmp.Count-1 to tmp.Length-1.
Jim Mischel
Was borrowing his code. Fixing it.
Matthew Scharley
judging by Edward's code, you need LastIndexOf()
Anton Tykhyy
The comments weren't in the question when I originally answered and it slipped though. Was fixed before you commented though.
Matthew Scharley
Fails on null input :-)
Robert Venables
So does the original. What can I say, do null checking.
Matthew Scharley
+2  A: 

I'd do it this way

    public static string GetTextAfterMarker(string line, string marker)
    {
        int pos = line.LastIndexOf(marker);
        if (pos < 0)
            return string.Empty;
        else
            return line.Substring(pos + marker.Length);
    }

No need to call split, no need to create an array of items, which basically you're just going to throw away.

Much faster and less resource heavy.

Binary Worrier
+2  A: 

If I'm reading your code correctly, you're trying to get the last instance?

string[] splits = line.Split(new string[] { marker }, StringSplitOptions.None);
return (splits.Length == 1) ? string.Empty : splits[splits.Length-1];
Jim Mischel
A: 

What about:

public static string GetTextAfterMarker(string line, string marker)
{
    var items = line.Split(new[] {marker}, StringSplitOptions.None);

    string result = items[items.Length-1];
    return line.Equals(result) ? string.Empty : result;
}
CMS
A: 

You could always use regular expressions instead of split:

    public static string GetTextAfterMarker(string line, string marker)
    {
        if (String.IsNullOrEmpty(line))
            throw new ArgumentException("line is null or empty.", "line");
        if (String.IsNullOrEmpty(marker))
            throw new ArgumentException("marker is null or empty.", "marker");
        string EscapedMarker = Regex.Escape(marker);
        return Regex.Match(line, EscapedMarker + "([^" + EscapedMarker + "]+)").Groups[1].Value;
    }
Robert Venables
+7  A: 
var    index = line.LastIndexOf (marker) ;
return index < 0 ? string.Empty : line.Substring (index + marker.Length) ;
Anton Tykhyy
+1: For bypassing split
Binary Worrier
+1  A: 

Altough I don't know wether you asked this out of simple curiosity, I would recommend you to not being afraid of creating auxiliary variables if that makes your code more readable. You will rarely degrade your code performance for using these extra variables.

Konamiman
it's just a lot of repeated code that I wanted to solve with the linq solution
Edward Tanguay
@Edward: Linq isn't always the anser, see Anton Tykhyy answer.
Binary Worrier