3

I looked through simular topic on this subject but got stuck, I need some help of VBA experts in excel 2003

I have a row of data as follows

 (Project Code)    ...(Milestone1)  (Date1)     (Milestone2)  (Date2) ... (Milestone 10)(Date3)
        ColumnA    ... ColumnAG     ColumnAH    ColumnAI     ColumnAJ  ...  ColumnAY    ColumnAZ
        Header1        Header33     Header34    Header35     Header36  ...  Header51    Header52
 rowkey datavalue1     datavalue33   datavalue34 datavalue35  datavalue36 ... datavalue51

I have a denormalized data set where the datavalues may or may not be empty on a row-by-row basis. I need to normalize them.

ie

        1234567        start    12/12/2012    build      12/01/2013 ... finish 01/02/2014        
        1234568        plan     14/12/2012    contract   02/02/2013  ... NULL   NULL

would become

        1234567        header33  start    12/12/2012
        1234567        header34  build    12/01/2013
        ...
        1234567        header51  finish   01/02/2014
        1234568        header33  plan     14/12/2012
        1234568        header34  contract 02/02/2013

I have about hundred of rows, containing project data with names of 10 milestones and 10 associated dates, and need to split these up so I have multiple rows listing the milestone header / milestone name / date per project code.... If the milestone name and data is blanc the record can be skipped...

There is a simular post Convert row with columns of data into column with multiple rows in Excel 2007 , but don`t know how to change to apply this to the above scenario...

Any help would be appreciated Thanks

Community
  • 1
  • 1
Geert
  • 31
  • 2

1 Answers1

0

Okay, the code for this really isn't too advanced, but it is a little tedious. I designed this one for you that should accomplish what you need. It is filled with commentary you can use to help learn the code as well (in case you want to do this again on your own). Good luck!

Sub normalizeData()

Dim i As Integer, j As Integer, k As Integer
Dim r As Integer
Dim bReport As Workbook, Report As Worksheet, Report2 As Worksheet
Dim m(0 To 9) As Integer 'This sets up the variables for your milestones
Dim mileString As String, dateString As String

Set Report = Excel.ActiveSheet
Set bReport = Report.Parent
Set Report2 = bReport.Worksheets.Add

'There are two ways to identify your milestones to the subroutine: either have some kind of identifier to search _
for within the value of the milestone cell; or define them explicitly. I'm going to do the latter since I don't know _
what might be in those cells.

m(0) = 33 'The column number for each milestone
m(1) = 35
m(2) = 37
m(3) = 39
m(4) = 41
m(5) = 43
m(6) = 45
m(7) = 47
m(8) = 49
m(9) = 51


r = Report.UsedRange.Rows.Count 'Get the last row number and assign it to a _
                                short variable (just to make things easier).

Report2.Cells(1, 1).Value = "Normalized Data" 'Placing a header row allows us to use used range without having _
                                                to worry about identifying the first iteration. If you've ever tried this _
                                                by yourself you probably know what I'm referring to.

With Report2.Range("A1:D1") 'Just makin it purdy
    .Merge
    .HorizontalAlignment = xlCenter
    .Interior.Color = RGB(0, 20, 99)
    .Font.Color = RGB(224, 238, 255)
    .Font.Bold = True
    .Font.Size = 14
End With


For i = 2 To r
    For j = 0 To 9
        If Report.Cells(i, m(j)).Value <> "" And _
        Report.Cells(i, m(j)).Value <> "Null" Then 'I'm not sure if the empty values will be null or blank strings.
            mileString = Report.Cells(i, m(j)).Value
            dateString = Report.Cells(i, m(j) + 1).Value
            k = Report2.UsedRange.Rows.Count + 1
            Report2.Cells(k, 1).Value = Report.Cells(i, 1).Value
            Report2.Cells(k, 2).Value = Report.Cells(1, m(j)).Value 'Assuming your header is on the first row
            Report2.Cells(k, 3).Value = mileString
            Report2.Cells(k, 4).Value = dateString
            Report2.Cells(k, 4).NumberFormat = "MM/dd/yyyy"
        End If
    Next j
Next i

For i = 2 To Report2.UsedRange.Rows.Count
    If i Mod 2 = 0 Then
        Report2.Range("A" & i & ":D" & i).Interior.Color = RGB(227, 235, 252) 'Alternating row color for easier reading.
    End If
Next i

'************** This part adds borders. Omit this block if you don't want them.************
With Report2.Range("A1:D" & Report2.UsedRange.Rows.Count)
    .Borders(xlEdgeTop).Weight = xlThin
    .Borders(xlEdgeBottom).Weight = xlThin
    .Borders(xlLeft).Weight = xlThin
    .Borders(xlRight).Weight = xlThin
    .Borders(xlInsideHorizontal).Weight = xlThin
    .Borders(xlInsideVertical).Weight = xlThin
End With
'******************************************************************************************



End Sub

PS: make sure you are on the denormalized worksheet when you run the macro. It is designed to reference that sheet as the active one and place the normalized data into a new worksheet.

Also note that I used an array to identify the milestones, but I'm sure you could just as easily use a For...Next loop with a Step 2 increment for improved scalability. For example, in this case, the beginning of your for loop would look like For j = 33 to 51 Step 2. This is assuming all your milestones are two columns apart.

Ross Brasseaux
  • 3,879
  • 1
  • 28
  • 48