Friday, May 6, 2011

Is there a cleaner way to write this block of code that parses a delimited string into an object?

Is there a more elegant way to structure the following code, which takes a comma delimited string containing certain values in specific positions and assigns the values to the properties of an object?

        // split comma delimited items into a string array
        Dim items As String() = myList.Split(CChar(","))

        // Assign my Person object's properties

        // Name
        If items.Length > 0 Then
            Person.FullName = items(ItemIndex.FullName)
        End If

        // Address1
        If items.Length > 1 Then
            Person.Address1 = items(ItemIndex.Address1)
        End If

        // Address2
        If payload.Length > 2 Then
            Person.Address2 = items(ItemIndex.Address2)
        End If

EDIT: I'm open to C# examples, too.

From stackoverflow
  • My first instinct is to question why the input is in the form of a comma-delimited string. Assuming that you don't control the input and just have to live with it, I don't have a huge problem with this implementation. While I'm not intimately familiar with VB.net, I think there is a StringTokenizer. I would take a look at that and consider using that instead.

  • Looks fine. Are you expecting variable length arrays? You could:

    If items.Length >= 3 Then
      // Assign properties
    Else
      Throw New NotSupportedException("The line needs to have three items");
    End If
    
  • You could use an enumerator but it's pretty much the same code just shorter.

    Dim items = myList.Split(",").GetEnumerator
    If items.MoveNext Then FirstName = items.Current
    If items.MoveNext Then Address1 = items.Current
    If items.MoveNext Then Address2 = items.Current
    

    It's pretty tough to validate coming from a comma delimited string.

    Of course if you are using objects you could pass something to your object and let your object parse it. I'm assuming you are able to use Linq in the next example.

    Public Class Person
        Private Enum Position
            FirstName = 0
            Address1 = 1
            Address2 = 2
            LastName = 3
        End Enum
        Sub FillFromArray(ByVal Values() As String)
            FirstName = If(Values.ElementAtOrDefault(Position.FirstName), String.Empty)
            Address1 = If(Values.ElementAtOrDefault(Position.Address1), String.Empty)
            Address2 = If(Values.ElementAtOrDefault(Position.Address2), String.Empty)
            LastName = If(Values.ElementAtOrDefault(Position.LastName), String.Empty)
        End Sub
        Public FirstName As String
        Public Address1 As String
        Public Address2 As String
        Public LastName As String
    End Class
    
    
    Module MainModule
        Sub Main()
            Dim testString As String = "FirstName,Address 1,Address 2"
            Dim testPerson As New Person
    
            testPerson.FillFromArray(testString.Split(","))
    
            Debug.Assert(testPerson.FirstName = "FirstName")
            Debug.Assert(testPerson.Address1 = "Address 1")
            Debug.Assert(testPerson.Address2 = "Address 2")
            Debug.Assert(testPerson.LastName = String.Empty)
    
        End Sub
    End Module
    
  • You could append a bunch of commas onto myList, and then you can skip the if statements:

    // split comma delimited items into a string array
    myList = myList & ",,," // One comma for every property.
    Dim items As String() = myList.Split(CChar(","))
    
    // Assign my Person object's properties
    Person.FullName = items(ItemIndex.FullName)
    Person.Address1 = items(ItemIndex.Address1)
    Person.Address2 = items(ItemIndex.Address2)
    
    Carl Manaster : +1 This is a bit of a hack, but I think it's the cleanest way to do it. Avoid all the conditionality by ensuring there are enough elements in the array.
    Eclipse : It's kind of hacky, but removing corner-cases by adding extra data is a pretty common technique. Another example is when you have a matrix and you need to apply a transformation to each cell based on its neighbours. Instead of writing extra code to handle the sides and corners, just surround the matrix with the identity value for the function and only iterate over the interior cells.
  • If you wanted to rely on ItemIndex names matching the property names (convention) then you could use reflection to make it completely declarative. I can elaborate if you think that's a route you're interested in...

0 comments:

Post a Comment