tags:

views:

749

answers:

13

Can anyone think of a nicer way to do the following:

public string ShortDescription
{
    get { return this.Description.Length <= 25 ? this.Description : this.Description.Substring(0, 25) + "..."; }
}

I would have liked to just do string.Substring(0, 25) but it throws an exception if the string is less than the length supplied.

A: 

It looks fine to me. I'm sure there are other places you could agonize over refactoring that would actually be worth it.

Tom Ritter
A: 

I think the approach is sound, though I'd recommend a few adjustments

  • Move the magic number to a const or configuration value
  • Use a regular if conditional rather than the ternary operator
  • Use a string.Format("{0}...") rather than + "..."
  • Have just one return point from the function

So:

public string ShortDescription
{
    get
    {
        const int SHORT_DESCRIPTION_LENGTH = 25;

        string _shortDescription = Description;

        if (Description.Length > SHORT_DESCRIPTION_LENGTH)
        {
            _shortDescription = string.Format("{0}...", Description.Substring(0, SHORT_DESCRIPTION_LENGTH));
        }

        return _shortDescription;
    }
}

For a more general approach, you might like to move the logic to an extension method:

public static string ToTruncated(this string s, int truncateAt)
{
    string truncated = s;

    if (s.Length > truncateAt)
    {
        truncated = string.Format("{0}...", s.Substring(0, truncateAt));
    }

    return truncated;
}

Edit

I use the ternary operator extensively, but prefer to avoid it if the code becomes sufficiently verbose that it starts to extend past 120 characters or so. In that case I'd like to wrap it onto multiple lines, so find that a regular if conditional is more readable.

Edit2

For typographical correctness you could also consider using the ellipsis character (…) as opposed to three dots/periods/full stops (...).

Richard Ev
aw, this is a great opportunity to use the ternary -- a one-liner.
Michael Paulukonis
Why do people hate on ternary so much?
Instantsoup
IMO, using the + operator seems more straight-forward in this case. Other two suggestions sound good to me.
Greg
I agree with most of your points, however it does make for bloated code. Easy on the eye but not much room for any other code.
Duke of Muppets
I find that bloat vs genericness vs readability is often a tough one. I try to use a style that favours consistency above all else, which means that short bits of code often get a little bloated.
Richard Ev
+ is faster than string.Format when elements are not repeated
Dmitri Nesteruk
I don't believe that this is a performance-related discussion, for me it's all about what is the most readable for the developer.
Richard Ev
+6  A: 

The way you've done it seems fine to me, with the exception that I would use the magic number 25, I'd have that as a constant.

Do you really want to store this in your bean though? Presumably this is for display somewhere, so your renderer should be the thing doing the truncating instead of the data object

MrWiggles
+12  A: 
return this.Description.substring(0,Math.Min(this.Description.length,25));

Doesn't have the ... part. Your way is probably the best, actually.

Welbog
I like your way better. If I was going to use the terniary operator, I'd probably do it on the length. However, Math.max is clearer.
Brian
I think you should use Math.Min not Math.Max here :-)
Rune Grimstad
I fixed his bug.
Brian
@Rune: Good catch.
Welbog
Like it! Less code, not sure if its more readable.
Duke of Muppets
I think it reads more closely to the actual intent of the method. You do lose the ellipsis at the end though.
Herms
No,no,no !This will create a copy of this.Description because the string class is immutable even if we have a short description ( < 25).
Petar Petrov
Unless this is used in a tight loop a million times, the copy of the description made if it is <25 chars is probably nothing to be concerned with.
sixlettervariables
@Petar - no it won't. String.Substring() returns the same string instance if the length is equal to the string length.
Dan C.
This code is not particularly readable...I think it is trying to be "too clever".Could also use some whitespace.
Richard Ev
+1  A: 

One way to do it:

int length = Math.Min(Description.Length, 25);
return Description.Substring(0, length) + "...";

There are two lines instead of one, but shorter ones :).

Edit: As pointed out in the comments, this gets you the ... all the time, so the answer was wrong. Correcting it means we go back to the original solution.

At this point, I think using string extensions is the only option to shorten the code. And that makes sense only when that code is repeated in at least a few places...

Dan C.
If you're only going to use a variable once, you can often get rid of it and simplify your code. E.g. Welblog's solution.
Brian
@Brian - that's up to him. One may argue that temporary variables make it easier for the reader...Oh, and they're the same answers, were written at the same time :)
Dan C.
BUT -- that gets you the "..." and every line. It should be included on when the string has been truncated.
James Curran
@James - good point. Well, correcting it gets us back to the original... pity.
Dan C.
A: 

Looks fine to me, being really picky I would replace "..." with the entity reference "&hellip;"

Nick Allen - Tungle139
Doesn't work so well if the text isn't going to be part of an XML/HTML/whatever document. Or do you mean a Unicode ellipsis character (if there is one)?
Jon Skeet
Agreed should have added if XML/HTML.... I have my web dev hat on
Nick Allen - Tungle139
Jon: Unicode Character 'HORIZONTAL ELLIPSIS' (U+2026)
James Curran
+9  A: 
public static Take(this string s, int i)
{
    if(s.Length <= i)
        return s
    else
        return s.Substring(0, i) + "..."
}

public string ShortDescription
{
    get { return this.Description.Take(25); }
}
marcumka
We've got basically the same solution, but I don't like the method "Take" because it's already "taken" for strings by LINQ, as string implements IEnumerable<char>
Jon Skeet
(Deleted my answer as it really is just the same as yours.)
Jon Skeet
A: 

I can't think of any but your approach might not be the best. Are you adding presentation logic into your data object? If so then I suggest you put that logic elsewhere, for example a static StringDisplayUtils class with a GetShortStringMethod( int maxCharsToDisplay, string stringToShorten).

However, that approach might not be great either. What about different fonts and character sets? You'd have to start measuring the actual string length in terms of pixels. Check out the AutoEllipsis property on the winform's Label class (you'll prob need to set AutoSize to false if using this). The AutoEllipsis property, when true, will shorten a string and add the '...' chars for you.

ng5000
I agree with you. This code should be in a display helper method. As the display length may need to vary from page to page. This code is geared towards a web application so no AutoEllipsis property but nice solution for windows apps.
Duke of Muppets
+11  A: 

I needed this so often, I wrote an extension method for it:

public static class StringExtensions
{
    public static string SafeSubstring(this string input, int startIndex, int length, string suffix)
    {
        // Todo: Check that startIndex + length does not cause an arithmetic overflow - not that this is likely, but still...
        if (input.Length >= (startIndex + length))
        {
            if (suffix == null) suffix = string.Empty;
            return input.Substring(startIndex, length) + suffix;
        }
        else
        {
            if (input.Length > startIndex)
            {
                return input.Substring(startIndex);
            }
            else
            {
                return string.Empty;
            }
        }
    }
}

if you only need it once, that is overkill, but if you need it more often then it can come in handy.

Edit: Added support for a string suffix. Pass in "..." and you get your ellipses on shorter strings, or pass in string.Empty for no special suffixes.

Michael Stum
I think this is the best answer (+1), but timing means I doubt it'll get the appropriate amount of votes, sadly.
Marc Gravell
Doesn't really matter that much - there is a good answer in already by marcumka which helps in this specific case, it could help people in the future when searching, and usually late stuff gets votes over time - i still get votes for 5 month old stuff.
Michael Stum
Won't remember to vote this up in 5 months, so I'm doing it now. ;-)
Tomalak
Arithmetic overflow on string indexes? Guess it could happen, but that would be one long string.
Kibbee
True, but just in case someone passes in int.MaxValue - be it intentional or not - there should be a proper handling (as in: OverflowException) instead of "weird" results
Michael Stum
A: 

I'd stick with what you have tbh, but just as an alternative, if you have LINQ to objects you could

new string(this.Description.ToCharArray().Take(25).ToArray())

//And to maintain the ...
+ (this.Description.Length <= 25 ? String.Empty : "...")

As others have said, you'd likely want to store 25 in a constant

MattH
+3  A: 
Josh
Cute! Not the most readable approach but like the not splitting words.
Duke of Muppets
I say it's not the most readable approach because I struggle to read and understand regular expressions. Other people may find it a breeze!
Duke of Muppets
Yeah, I'm not the best at writing Regex either, so I'm sure there are improvements that could be made there.
Josh
A: 

You should see if you can reference the Microsoft.VisualBasic DLL into your app so you can make use of the "Left" function.

Kibbee
+1  A: 

without .... this should be the shortest :

public string ShortDescription
{
    get { return Microsoft.VisualBasic.Left(this.Description;}
}
dr. evil
Ahh the trusty VB left function. Why did that never make it into the String class?!
Duke of Muppets