I have a very messy excel sheet that I'm trying to reformat into something readable. Currently, it is structured as such (each large separation resembles a new cell):
Title1 Var1 Var1_Value Var1.1 Var1.1_Value ... Var1.K Var1.K_Value
Title2 Var2 Var2_Value Var2.1 Var2.1_Value ... Var2.L Var2.L_Value
...
TitleM VarM VarM_Value VarM.1 VarM.1_Value ... VarM.N VarM.N_Value
To clarify, the amount of variables and values per column varies for each row, however every variable will have a value. Ultimately, my end goal is to create something formatted as such:
Title1 Var1 Var1_Value
Title1 Var1.1 Var1.1_Value
...
TitleM VarM.N VarM.N_Value
Where the Title string is repeated for each Var and Var_Value in its row.
I don't know a lot about VBA, so I'm looking for help on the best avenue to achieve this formatting. Here is my thought process in psuedocode below, I tried to format to be VBA-esque when I could.
for idx = 1 To lastRow
' Will likely have to create a function to find
' last filled column in a row -- lastColForRow
tempArray = data(idx,2 To lastColforRow(idx))
for jdx = 1 To length(tempArray)-1 Step 2
newCell(end+1,1) = data(idx,1)
newCell(end+1,2) = tempArray(j)
newCell(end+1,3) = tempArray(j+1)
next jdx
next idx
This code should do it (note that it assumes that there is no header row)
Public Sub Reformat()
Dim lastrow As Long
Dim lastcol As Long
Dim numrows As Long
Dim i As Long, ii As Long
Application.ScreenUpdating = False
With ActiveSheet
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = lastrow To 1 Step -1
lastcol = .Cells(i, .Columns.Count).End(xlToLeft).Column
'integer division so as to get the number of value pairs
numrows = lastcol \ 2
'only do anything if we have more than one value pair
If numrows > 1 Then
'insert extra rows for extra value pairs
.Rows(i + 1).Resize(numrows - 1).Insert
'copy the titles down to all new rows
.Cells(i, "A").Copy .Cells(i, "A").Resize(numrows)
'a value pair at a time, cut and copy to next new row
For ii = 4 To lastcol Step 2
'target row is current row (i) + the value pair index ((ii /2)-1)
.Cells(i, ii).Resize(, 2).Cut .Cells(i + (ii / 2) - 1, "B")
Next ii
End If
Next i
End With
Application.ScreenUpdating = True
End Sub
This does it with arrays onto a new sheet
Sub climatefreak()
Dim lastrow&
Dim ws As Worksheet
Dim lastcolumn&
Dim idx&
Dim ClmIdx&
Dim tws As Worksheet
Dim i&
Dim trw&
Set tws = Sheets("Sheet3")
Set ws = ActiveSheet
With ws
lastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
For idx = 1 To lastrow
Dim temparr
lastcolumn = .Cells(idx, .Columns.Count).End(xlToLeft).Column
temparr = Range(.Cells(idx, 1), .Cells(idx, lastcolumn)).Value
For i = LBound(temparr, 2) + 1 To UBound(temparr, 2) Step 2
trw = tws.Range("A" & tws.Rows.Count).End(xlUp).Row + 1
tws.Cells(trw, 1) = temparr(UBound(temparr, 1), 1)
tws.Cells(trw, 2).Resize(, 2) = Array(temparr(1, i), temparr(1, i + 1))
Next i
Next idx
End With
End Sub
Related
There is a table with each row needs to be separated so as there is only one name/date/transfer method in a cell in each row. I was able to separate it by names but I struggle to get the dates and e-mail-thing right, because sometimes the cells in respective columns are empty:
Desired result
I did this for names:
Sub splitcells()
Dim splitVals As Variant
Dim totalVals As Long
Set sh1 = ThisWorkbook.Sheets(1)
Set sh2 = ThisWorkbook.Sheets(2)
lrow1 = sh1.Range("A65356").End(xlUp).Row
For j = 2 To lrow1
splitVals = split(sh1.Cells(j, 2), Chr(10))
For i = LBound(splitVals) To UBound(splitVals)
lrow2 = sh2.Range("B65356").End(xlUp).Row
lrow3 = sh2.Range("A65356").End(xlUp).Row
sh2.Cells(lrow3 + 1, 1) = sh1.Cells(j, 1)
sh2.Cells(lrow3 + 1, 2) = splitVals(i)
Next i
Next j
End SubI tried to do the same thing for the rest by moving all the column indicators to column 3 like below, but it doesn`t work properly, filling dates in every cell in a column while some of them should be empty:
Sub splitcells2()
Dim splitVals As Variant
Dim totalVals As Long
Set sh1 = ThisWorkbook.Sheets(1)
Set sh3 = ThisWorkbook.Sheets(3)
lrow1 = sh1.Range("A65356").End(xlUp).Row
For j = 2 To lrow1
splitVals = split(sh1.Cells(j, 3), Chr(10))
For i = LBound(splitVals) To UBound(splitVals)
lrow2 = sh3.Range("B65356").End(xlUp).Row
lrow3 = sh3.Range("A65356").End(xlUp).Row
sh3.Cells(lrow3 + 1, 1) = sh1.Cells(j, 1)
sh3.Cells(lrow3 + 1, 2) = splitVals(i)
Next i
Next j
End Sub
If I understand you correctly, maybe something like this ?
Sub test()
Dim splitVals As Variant
Set sh1 = ThisWorkbook.Sheets(1)
Set sh2 = ThisWorkbook.Sheets(2)
colcount = 6 'change if not the same with the actual table
lrow1 = sh1.Range("A65356").End(xlUp).Row
For j = 2 To lrow1
Set oFill = sh2.Range("A65356").End(xlUp).Offset(1, 0)
With sh1.Cells(j, 2)
If InStr(.Value, Chr(10)) Then
cnt = Len(.Text) - Len(Replace(.Text, Chr(10), "")) + 1
Set oFill = oFill.Resize(cnt, 1)
End If
End With
For i = 1 To colcount
With sh1.Cells(j, i)
If InStr(.Value, Chr(10)) Then
splitVals = Application.Transpose(Split(.Value, Chr(10)))
Else
splitVals = .Value
End If
End With
oFill.Offset(0, i - 1).Value = splitVals
Next i
Next j
End Sub
The code has two loop. The first, loop to each row in column A, the second, loop to each column of the table.
At the first loop, it check if the looped cell offset(j,2) has a line break then it set the oFill (the target cell to be filled) to resize as much as the rows needed.
At the second loop, it check if the looped cell has a line break then it get the value of the looped cell with split function as splitVals variable. If no line break, the splitVals value is the same with the looped cell. Then finally it put the splitVals to the oFill range. Do the same with the rest of the column.
Please be noticed, the code assumes that if in column B there are N names, then the rest of the column (same row) value is either with N lines or blank.
After from VBasic2008 help to my code, please change this line:
lrow1 = sh1.Range("A65356").End(xlUp).Row
Set oFill = sh2.Range("A65356").End(xlUp).Offset(1, 0)
into something like this :
lrow1 = sh1.Range("A" & rows.count).End(xlUp).Row
Set oFill = sh2.Range("A" & rows.count).End(xlUp).Offset(1, 0)
Is there a way to make my VBA code work for my macro? I want my macro's if function to read the first column of each worksheet in my excel (it has as many sheets as days in the exact month i'm working on), read through each cell and if the currently read cell is equal to or larger than '15 minutes compared to the first cell, then the code would execute, otherwise go to the next cell in the first column.
This is the format of the worksheets i'm working on:
TimeStamp
Power Consumption
Power Production
Inductive Power Consumption
2021.01.01. 8:12:38 +00:00
747
575
3333
2021.01.01. 8:17:35 +00:00
7674
576
3333
... etc ,
And my code looks something like this:
Sub stackoverflow()
Dim w As Integer 'index of worksheets
Dim i As Integer 'row index that steps through the first column
Dim t As Integer 'reference row index i inspect the time to
Dim x As Integer 'row index where i want my data to be printed
Dim j As Integer 'col index
Dim Timediff As Date 'not sure if this is even needed
t = 2
j = 1
x = 1
'Timediff = ("00:15:00")
For w = 3 To ActiveWorkbook.Worksheets.Count 'for every sheet from the 3rd to the last
lRow = ActiveWorkbook.Worksheets(w).Cells(Rows.Count, 1).End(xlUp).Row 'find the last row in each worksheet
lCol = ActiveWorkbook.Worksheets(w).Cells(1, Columns.Count).End(xlToLeft).Column 'find the last column in each worksheet
For x = 2 To lRow
For i = 2 To lRow
'If the time in cell(i,j) is >= then cell(t,j) + 15 minutes,
If Cells(i, j) >= DateAdd("n", 15, Cells(t, j)) Then
ActiveWorkbook.Worksheets(w).Range(i, j).Copy ActiveWorkbook.Worksheets(2).Range(x, j)
ActiveWorkbook.Worksheets(w).Range(i, j + 1).Copy ActiveWorkbook.Worksheets(2).Range(x, j + 1)
'put the new reference point after the found 15 minute mark
t = i + 1
Else
End If
Next i
Next x
Next w
End Sub
So all in all I want my code to notice when the first column reaches a 15 minute mark, and execute some code (subtracting the values of the 15 minute mark from the reference where it started, put the value in the'2nd sheet, and then step to the next cell, and repeat the process).
I'm not entirely sure which information you are attempting to copy to the second worksheet but the following code should be able to get you there pretty easily. Additionally, I've added a function that will fix the format of your TimeStamp field so that excel will recognize it and we can then do math with it
Sub TestA()
Dim xlCellA As Range
Dim xlCellB As Range
Dim xlCellC As Range
Dim i As Integer
Dim j As Integer
Dim lRow As Long
Dim lCol As Long
Set xlCellA = ActiveWorkbook.Worksheets(2).Cells(2, 1)
For i = 3 To ActiveWorkbook.Worksheets.Count
lRow = ActiveWorkbook.Worksheets(i).Cells.SpecialCells(xlCellTypeLastCell).Row
lCol = ActiveWorkbook.Worksheets(i).Cells.SpecialCells(xlCellTypeLastCell).Column
Set xlCellB = ActiveWorkbook.Worksheets(i).Cells(2, 1)
xlCellB.Value = FixFormat(xlCellB.Value)
xlCellB.Offset(0, lCol + 1).Value = "=DATEVALUE(MID(" & xlCellB.Address & ",1,10))+TIMEVALUE(MID(" & xlCellB.Address & ",12,8))"
For j = 3 To lRow
Set xlCellC = ActiveWorkbook.Worksheets(i).Cells(j, 1)
xlCellC.Value = FixFormat(xlCellC.Value)
xlCellC.Offset(0, lCol + 1).Value = "=DATEVALUE(MID(" & xlCellC.Address & ",1,10))+TIMEVALUE(MID(" & xlCellC.Address & ",12,8))"
If xlCellC.Offset(0, lCol + 1) - xlCellB.Offset(0, lCol + 1) >= ((1 / 24) / 4) Then
With xlCellA
.Value = xlCellC.Value
.Offset(0, 1).Value = xlCellC.Offset(0, 1).Value
End With
Set xlCellA = xlCellA.Offset(1, 0)
End If
Next j
Next i
Set xlCellA = Nothing
Set xlCellB = Nothing
Set xlCellC = Nothing
End Sub
Private Function FixFormat(ByVal dStr As String) As String
Dim tmpStr As String
Dim i As Integer
For i = 1 To Len(dStr)
If Mid(dStr, i, 1) <> "." Then
tmpStr = tmpStr & Mid(dStr, i, 1)
Else
If Mid(dStr, i + 1, 1) <> " " Then tmpStr = tmpStr & "-"
End If
Next i
FixFormat = tmpStr
End Function
It's not really clear what needs to happen when the 15min threshold is met but this should get you most of the way there:
Sub stackoverflow()
Dim w As Long, Timediff As Double
Dim wb As Workbook, wsData As Worksheet, wsResults As Worksheet, col As Long
Dim baseRow As Range, dataRow As Range, rngData As Range, resultRow As Range
Timediff = 1 / 24 / 4 '(15min = 1/4 of 1/24 of a day)
Set wb = ActiveWorkbook 'or ThisWorkbook
Set wsResults = wb.Worksheets("Results")
'first row for recording results
Set resultRow = wsResults.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow
For w = 3 To wb.Worksheets.Count 'for every sheet from the 3rd to the last
Set rngData = wb.Worksheets(w).Range("A1").CurrentRegion 'whole table
Set rngData = rngData.Offset(1, 0).Resize(rngData.Rows.Count - 1) 'exclude headers
Set baseRow = rngData.Rows(1) 'set comparison row
For Each dataRow In rngData.Rows 'loop over rows in data
If (dataRow.Cells(1).Value - baseRow.Cells(1).Value) > Timediff Then
resultRow.Cells(1).Value = dataRow.Cells(1) 'copy date
For col = 2 To dataRow.Cells.Count 'loop columns and subtract
resultRow.Cells(col).Value = _
dataRow.Cells(col).Value - baseRow.Cells(col).Value
Next col
Set resultRow = resultRow.Offset(1, 0)
Set baseRow = dataRow.Offset(1, 0) 'reset comparison row to next row
End If
Next dataRow
Next w
End Sub
Currently, I want to automate some annoying work in excel and need some help.
I have a huge report which has 200k+ rows and about 500 columns and my task is to find out which cells of a column are never used.
This was fairly easy and I managed it to create a script that works for that so far.
But now I want to distinguish between row types and return for each row type whether there are columns that are never used.
My problem is that I do not know how to iterate through the contents of a cell, so that if the row type changes my script will count the empty columns for the next row type.
I hope you get the idea and can help me. You do not have to give me the full code but maybe an idea of how I can get to the solution :)
This is the vba code I currently have and that gives me the correct solution but without distinguishing between the rowtypes
Public row As Long
Public rowMax As Long
Public startRow As Integer
Public materialType As String
Public filter As String
Public col As Integer
Public colMax As Integer
Public isUsed As Boolean
Sub bestimmeObFelderGenutzt()
With Sheets("Sheet1")
filter = "I"
startRow = 2
rowMax = Sheets("Sheet1").Cells(.Rows.Count, "F").End(xlUp).row
colMax = Sheets("Sheet1").Cells(1, .Columns.Count).End(xlToLeft).Column
materialType = Sheets("Sheet1").Range(filter & startRow).Value
For col = 1 To colMax
Sheets("Sheet2").Cells(1, col + 2).Value = Sheets("Sheet1").Cells(1, col).Value
Next col
For row = 2 To rowMax
Sheets("Sheet2").Range("A" & row).Value = Sheets("Sheet1").Range("A" & row).Value
Sheets("Sheet2").Range("B" & row).Value = Sheets("Sheet1").Range("I" & row).Value
For col = 1 To colMax
If IsEmpty(Sheets("Sheet1").Cells(row, col)) = False Then
isUsed = True
Sheets("Sheet2").Cells(row, col + 2).Value = 1
Else:
Sheets("Sheet2").Cells(row, col + 2).Value = 0
End If
Next col
Next row
End With
End Sub
Sub findeUngenutzteSpalten()
With Sheets("Sheet2")
rowMax = Sheets("Sheet2").Cells(.Rows.Count, "F").End(xlUp).row
colMax = Sheets("Sheet2").Cells(1, .Columns.Count).End(xlToLeft).Column
Sheets("Sheet3").Cells(1, 1).Value = "Spaltenüberschrift"
Sheets("Sheet3").Cells(1, 2).Value = "Jemals benutzt?"
For col = 3 To colMax
isUsed = False
For row = 2 To rowMax
If Sheets("Sheet2").Cells(row, col).Value = 1 Then
Sheets("Sheet3").Range("A" & col - 1).Value = Sheets("Sheet2").Cells(1, col).Value
Sheets("Sheet3").Range("B" & col - 1).Value = "Ja"
GoTo WeiterCol
Else:
If row = rowMax Then
Sheets("Sheet3").Range("A" & col - 1).Value = Sheets("Sheet2").Cells(1, col).Value
Sheets("Sheet3").Range("B" & col - 1).Value = "Nein"
Else:
GoTo WeiterRow
End If
End If
WeiterRow:
Next row
WeiterCol:
Next col
End With
End Sub
If I understood your task correctly this should work, copy to your module and read comments:
Sub FindUnusedColumnsPerRow()
Dim cellRow As range, cellColumn As range
Dim rowRange As range, columnRange As range
Dim rowsCount As Long, columnsCount As Long
Dim insertRow As Long
Dim listOfEmptyColumns()
Dim i As Long, j As Long
Dim arrayCheck As Integer
With Sheets("Sheet1") ' I assume that this is your sheet with materials where you want to find unused columns
rowsCount = .Cells(Rows.Count, 6).End(xlUp).row ' get last row
columnsCount = .Cells(1, Columns.Count).End(xlToLeft).Column ' get last column
For Each cellRow In range(.Cells(2, 1), .Cells(rowsCount, 1)) ' going through all rows - here I suppose that material type is in the 1-st column, 1-st row is a header and data starts from 2-d row
For Each cellColumn In range(.Cells(cellRow.row, 2), .Cells(cellRow.row, columnsCount)) ' for each row looking through all columns - I suppose that data starts from 2-d column
If cellColumn = "" Then ' if the cell is empty.
ReDim Preserve listOfEmptyColumns(i) ' expanding array when needed
listOfEmptyColumns(i) = cellColumn.Column ' adding column number to an array, you may change it to = .cells(1,cellColumn.Column) to put a header name instead of column number
i = i + 1 ' increment the counter
End If
Next
On Error Resume Next ' a small trick to check whether the array with column numbers is empty
arrayCheck = UBound(listOfEmptyColumns) ' if the array is empty - an #9 "Subscript out of range" exception will be thrown
If Err.Number = 0 Then ' error number is 0 - means that there was no error
With Sheets("Sheet2") ' I suppose this is the sheet to store results
insertRow = .Cells(Rows.Count, 1).End(xlUp).row + 1 ' find the row to insert
.Cells(insertRow, 1) = cellRow.Value ' put the type to 1-st column
j = 2 ' start filling the row of the type with numbers of empty columns
For i = 0 To UBound(listOfEmptyColumns) ' populating data from array
.Cells(insertRow, j) = listOfEmptyColumns(i)
j = j + 1
Next
End With
End If
Err.Clear ' clearing the error, because it still holding an error information (if it was thrown)
On Error GoTo 0 ' don't forget to switch on normal error handling
Erase listOfEmptyColumns ' reset array before next row as the data is stored on sheet2
i = 0 ' reset the counter for further use
Next
End With
End Sub
I am having an issue getting array values to compare to values stored in cells on the spreadsheet.
I have tried having the cell value compare directly to the array value, but the check fails every time.
To attempt to correct this issue I have tried assigning the cell value on each iteration to a variable dimmed as varient (Just as the array is dimmed a varient)
Values are added to the array successfully and the varient type is used as some invoices are numbers only while others include letters.
When I walk through my code the variable is not being assigned/accepting a value. Every time the comparison statement is reached the variable shows that it is empty.
Dim Paidlrow As Long
Dim lrow As Long
Dim wb As Workbook
Dim Consolid As Worksheet
Dim PaidInv As Worksheet
Dim Summary As Worksheet
Dim Invoices() As Variant
Dim InvCheck As Variant
Dim txt As String
Dim Formula As String
Dim i As Long
Dim j As Long
Dim k As Long
Dim l As Long
Dim Cleared As Long
Dim LInv As Long
Dim NewBlank As Long
Dim MaxSheets As Integer
Set wb = Workbooks("Wire Payments projections for Euro's")
Set Consolid = wb.Sheets("Consolidation")
Set Summary = wb.Sheets("Pay Summary")
Set PaidInv = wb.Sheets("Paid Invoices")
'define define and define
MaxSheets = wb.Sheets.Count
lrow = Consolid.Cells(Rows.Count, 1).End(xlUp).Row + 1
Cleared = 1
ReDim Preserve Invoices(1 To Cleared)
i = 2
With wb
'begin inv extraction loop
For i = 2 To lrow
ReDim Preserve Invoices(1 To Cleared)
'if inv is marked for payment, add to array and move details to paid inv tab
With PaidInv
Paidlrow = .Cells(Rows.Count, 1).End(xlUp).Row + 1
End With
With Consolid
If .Cells(i, 10) = "X" Or .Cells(i, 10) = "x" Then
Invoices(Cleared) = .Cells(i, 1)
Consolid.Rows(i).Copy Destination:=PaidInv.Cells(Paidlrow, 1)
Consolid.Rows(i).Clear
Cleared = Cleared + 1
End If
End With
Next i
End With
'loop through each sheet to remove paid invoices identifie in previous loop
For k = 1 To MaxSheets
If wb.Sheets(k).Name <> Summary.Name And wb.Sheets(k).Name <> PaidInv.Name And wb.Sheets(k).Name <> Consolid.Name Then
With wb.Sheets(k)
LInv = Cells(Rows.Count, 2).End(xlUp).Row + 1
For j = LBound(Invoices) To UBound(Invoices)
For l = 7 To LInv
InvCheck = .Cells(l, 2).Value
If Invoices(j) = InvCheck And InvCheck <> "" Then
'.Rows(l).Delete
NewBlank = Cells(Rows.Count, 1).End(xlUp).Row + 1
.Range("A7:K7").Copy
.Range(.Cells(NewBlank, 1), .Cells(NewBlank, 11)).PasteSpecial Paste:=xlPasteFormats
'.Cells(NewBlank, 1) = Right(.Cells(1, 9), 6)
'Formula = "=$B$3*I"
'Formula = Formula & NewBlank
'.Cells(NewBlank, 10).Formula = Formula
End If
Next l
Next j
End With
End If
Next k
I have commented out code for the ease of testing. With the way it is now it should format some additional cells to match the formatting above it.
UPDATE
For kicks and giggles, I changed the Array and associated variable check to String type rather than variant. For some reason, this fixed the issue I was having. I am so confused...
There seems to be a dot missing:
With wb.Sheets(k)
LInv = Cells(Rows.Count, 2).End(xlUp).Row + 1
should be:
With wb.Sheets(k)
LInv = .Cells(Rows.Count, 2).End(xlUp).Row + 1
to ensure that LInv is read from sheet number k.
As it is, the code is equivalent to:
With wb.Sheets(k)
LInv = ActiveSheet.Cells(Rows.Count, 2).End(xlUp).Row + 1
and, if the active sheet doesn't have any values in the cells you are looking at, the comparison will fail.
There's a similar issue with this line later on in the code:
NewBlank = Cells(Rows.Count, 1).End(xlUp).Row + 1
should be:
NewBlank = .Cells(Rows.Count, 1).End(xlUp).Row + 1
I have some values (derived out of a formula) in a particular row in an excel sheet. I am trying to drag them down till a certain number of rows using .FillDown method of Range looping through all the columns.
Sub RawDataPreparation()
Dim v As Long
Dim LastRow As Long
Dim lastCol As Integer
Dim c As Integer
c = ThisWorkbook.Sheets("Metadata").Range("B10")
Dim ColName() As String
ReDim ColName(c)
Dim strFormulas() As Variant
ReDim strFormulas(c)
Dim lastColumn As Integer
With ThisWorkbook.Sheets("Raw Data")
lastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
For i = 0 To c - 1
On Error Resume Next
ColName(i) = ThisWorkbook.Sheets("Metadata").Range("C" & i + 10) 'copy the value corresponding to column C10:C18 in Metadata sheet to the array
ThisWorkbook.Sheets("Raw Data").Cells(1, lastColumn + 1 + i).Value = ColName(i)
Next i
With ThisWorkbook.Sheets("Raw Data")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For j = 0 To c - 1
On Error Resume Next
strFormulas(j) = ThisWorkbook.Sheets("Metadata").Range("D" & j + 10) 'copy the formulas corresponding to column D10:D18 in Metadata sheet to the array
With ThisWorkbook.Sheets("Raw Data")
.Cells(2, lastColumn + 1 + j).Formula = strFormulas(j)
End With
Next j
'Code to drag down the formula till last row.
For k = 0 To c - 1
ThisWorkbook.Sheets("Raw Data").Range(Cells(2, lastColumn + 1 + k), Cells(LastRow, lastColumn + 1 + k)).FillDown
Next k
End Sub
When I execute using F8 (step by step) the last loop (to drag the formula till last row) is being executed and giving intended result. But upon executing the entire Sub RawDataPreparation using F5, the last loop is getting omitted.
I am not able to understand this behavior. Can anyone suggest why this is happening?
You need to qualify references to Cells, Range, Rows, etc unless you KNOW that your ActiveSheet is the sheet being referred to.
So change your last statement to
ThisWorkbook.Sheets("Raw Data").Range(ThisWorkbook.Sheets("Raw Data").Cells(2, lastColumn + 1 + k), ThisWorkbook.Sheets("Raw Data").Cells(LastRow, lastColumn + 1 + k)).FillDown
Or, to make it a bit easier to read:
With ThisWorkbook.Sheets("Raw Data")
.Range(.Cells(2, lastColumn + 1 + k), .Cells(LastRow, lastColumn + 1 + k)).FillDown
End With
In order to make your code work the last loop should define the range somewhat like this:-
Dim k As Long
For k = 0 To C - 1
With ThisWorkbook.Sheets("Raw Data")
.Range(.Cells(2, lastColumn + 1 + k), .Cells(lastRow, lastColumn + 1 + k)).FillDown
End With
Next k
Observe the period before the Cells defining the range. This period makes the references refer to the "Raw Data" sheet, whereas in your code, in the absence of any spacification, they refer to the ActiveSheet. So you get a different result not depending upon how you trigger the code but which is the active sheet at the time.
Anyway, to prove my point I had to almost rewrite your code. I continued to finish the job, and here it is.
Sub RawDataPreparation()
' 24 Mar 2017
Dim WsMetadata As Worksheet
Dim WsRawData As Worksheet
Dim firstMetaRow As Long, TransferRow As Long
Dim nextCol As Integer
Dim lastRow As Long
Dim R As Long, C As Long
firstMetaRow = 10 ' set this value here instead of in the code
Set WsMetadata = ThisWorkbook.Sheets("Metadata")
TransferRow = CLng(Val(WsMetadata.Range("B10").Value)) - 1
' B10 can't have a value < 1. In your example it should be 9
If TransferRow < 0 Then Exit Sub
Set WsRawData = ThisWorkbook.Sheets("Raw Data")
With WsRawData
nextCol = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
End With
' transpose columns C and D of WsMetaData to rows 1 and 2 in WsRawData
For R = firstMetaRow To (firstMetaRow + TransferRow)
With WsMetadata
.Cells(R, "C").Copy Destination:=WsRawData.Cells(1, (nextCol + R - firstMetaRow))
.Cells(R, "D").Copy Destination:=WsRawData.Cells(2, (nextCol + R - firstMetaRow))
End With
Next R
With WsRawData
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.Range(.Cells(2, nextCol), .Cells(lastRow, nextCol + TransferRow)).FillDown
End With
End Sub
It works, but unfortunately it may not do the job you envisioned. The formulas you copy from the Metadata contain references to that sheet which can't be translated 1:1 once the range is transposed. You may have to settle for pasting values or translate the formulas before filling them down.