3
\$\begingroup\$

Here is a zero pad function I have:

''' <summary>
''' Take a number string and zero pad it to the specified length.
''' </summary>
''' <param name="Number">The string representation of the value that will be zero-padded.</param>
''' <param name="ToLength">The length of the string that will result after the zero-padding.</param>
''' <param name="TrimExtraZeroes">An optional boolean which, when true, indicates that any 
''' extra leading zeroes that make the string longer than the desired length should be removed.</param>
''' <returns>A value which is the same as <paramref name="Number" /> but zero padded to the length specified by <paramref name="ToLength" />.</returns>
''' <remarks></remarks>
Friend Function ZeroPad(ByVal Number As String, ByVal ToLength As Integer, Optional ByVal TrimExtraZeroes As Boolean = False) As String
    Const ZeroChar = "0"c

    Select Case Number.Length
        Case Is < ToLength
            Return New String(ZeroChar, ToLength - Number.Length) & Number
        Case Is = ToLength
            Return Number
        Case Is > ToLength
            ZeroPad = Number

            If (TrimExtraZeroes) Then
                Do While ZeroPad.StartsWith(ZeroChar) AndAlso ZeroPad.Length > ToLength
                    ZeroPad = ZeroPad.Substring(1)
                Loop
            End If
        Case Else
            'This case can never occur, but it's here to suppress compiler warning.
            Throw New InvalidOperationException(String.Format("Unknown number length: {0}", Number.Length))
    End Select
End Function

Now, focusing on just one portion of it -- the part that trims leading zeros:

Do While ZeroPad.StartsWith(ZeroChar) AndAlso ZeroPad.Length > ToLength
    ZeroPad = ZeroPad.Substring(1)
Loop

is there a more efficient, cleaner, or "slick" way to do this other than looping?

\$\endgroup\$
  • \$\begingroup\$ Why is Number a String? I would recommend you store all numbers as their proper type (int, double) and convert them to string only when displaying them on screen. This would simplify your function a lot. \$\endgroup\$ – the_lotus Jan 9 '15 at 14:54
  • 1
    \$\begingroup\$ @the_lotus -- I wrote this function a long time ago originally to zero-pad client and matter "numbers" in software I write for the law firm where I work. A client or matter "number" can actually have alphabetical characters, e.g. "045CN". I agree that it's improperly named, but I use it out of an old code snippet that I've never revisited. I'll probably change it to "numericalTextValue" or something like that. \$\endgroup\$ – rory.ap Jan 9 '15 at 14:59
6
\$\begingroup\$

Naming

Based on the naming guidelines parameters should be named using camelCase casing.

Select Case

If you omitt the Case Is > ToLength and the else part aka having only 2 cases you will have

Friend Function ZeroPad(ByVal Number As String, ByVal ToLength As Integer, Optional ByVal TrimExtraZeroes As Boolean = False) As String
    Const ZeroChar = "0"c

    Select Case Number.Length
        Case Is < ToLength
            Return New String(ZeroChar, ToLength - Number.Length) & Number
        Case Is = ToLength
            Return Number
     End Select

     If (TrimExtraZeroes) Then
         Do While Number.StartsWith(ZeroChar) AndAlso Number.Length > ToLength
             Number = Number.Substring(1)
         Loop
     End If
    Return Number
End Function  

Why don't you use the built in String.PadLeft() method which would obsolete the Select Case. In addition to first stripping any 0 of the left side of Number you won't need the loop at all.

This boils down to only this

Friend Function ZeroPad(ByVal Number As String, ByVal ToLength As Integer, Optional ByVal TrimExtraZeroes As Boolean = False) As String
    Const ZeroChar = "0"c
    If (TrimExtraZeroes) Then
        Number = Number.TrimStart({ZeroChar})
    End If
    Return Number.PadLeft(ToLength, ZeroChar)
End Function  

Update
Some timetables for calling both methods 1.000.000 times

                                    Yours(ms)  Mine(ms)
ZeroPad("0000000001", 2, False)       227       231
ZeroPad("0000000001", 2, True)       1696       297
ZeroPad("01", 2, False)               251       235
ZeroPad("01", 2, True)                248       263  
ZeroPad("01", 12, False)              256       247
ZeroPad("01", 12, True)               257       260 
\$\endgroup\$
  • \$\begingroup\$ Interesting data, that it's faster to trim all the leading zeros first and build it back up. \$\endgroup\$ – rory.ap Jan 9 '15 at 15:07
4
\$\begingroup\$

So, what does this function do again? Does it leftpad a number with zeros, or does it strip them away? It does both and that breaks the single responsibility principle. Kind of...

You could rename it FormatString and then its name would reflect it's one responsibility. The only problem with that is we already have String.Format, but I'll get back to that. First, I want to talk about this.

ByVal Number As String

Number. As. String.

Let that sink in for a second. You're passing a Number into your function as a String. If that doesn't smell funny, I'm not quite sure what does. In fact, your function doesn't care at all if what it's padding is a number or not.

Console.WriteLine(ZeroPad("HelloWorld",20))
' 0000000000HelloWorld

I'm not sure if that's expected behavior or not, but I'm not sure it matters. I don't really think you need a special function to accomplish what you're doing. Just take your number and Format it. If the data is coming in as a string, then simply cast it to a value first.

Dim originalValue As String = "01234"
Dim newValue As Integer

If Int32.TryParse(originalValue, newValue) Then
    Dim finalValue As String 
    finalValue = newValue.ToString("0000000000")

    Console.WriteLine(finalValue)
End If

If TryParse fails, you could return the original string. If you don't want the original value if it's not a number, you could use Convert.ToInt32 and catch the appropriate exceptions.

TL;DR I don't think you need to do this at all. Sorry.


It just occured to me that you might not want to repeat this code each time you need to pad to a different length, so you could wrap it in a function like this. (Even though I still hate passing in a number as a string.)

Friend Shared Function ZeroPad(ByVal Number As String, ByVal ToLength As Integer)
    Dim value As Integer

    If Int32.TryParse(Number, value) Then
        Return value.ToString(new String("0", ToLength))
    Else
        Return Number
    End If
End Function

IDEOne here

\$\endgroup\$
0
\$\begingroup\$

You can loop and count the characters until zeropad.charAt(count) isn't ZeroChar or the remaining length is equal to the desired length and then take the substring(count) to return

\$\endgroup\$
  • \$\begingroup\$ That would trim all leading ZeroChar characters instead of just the number that makes the length equal to Tolength. If that were what I wanted to do, I'd just use ZeroPad.TrimStart(ZeroChar). \$\endgroup\$ – rory.ap Jan 8 '15 at 17:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.