Assistance with slow VBA - excel

This code has taken a couple of hours so far and less than 10% complete, please advise how I can make this faster?
I have tried to explain the code by using comments in the code
Sheet1 has nearly 500k rows on dates, from 1 July 1990 to 30/6/2017 in 30 minute intervals, i.e. 48 rows per day.
I have a table on a different worksheet with a row of 12 columns displaying months 7 to 6, then below the month number in 3 rows are 3 different years
Sub Test2()
Application.ScreenUpdating = False
'Sheet1 contains the main data set
'Sheet3 contains a table with 12 columns and 3 rows
'Sheet2 is an output sheet
'Sheet4 is an output sheet
'Sheet5 is an output sheet
Dim i As Long 'main sheet rows (Sheet1 473,379 rows)
Dim j As Long 'Columns (Table of dates with 12 columns on sheet3)
Dim LastRowMain As Long 'Last row of sheet 1
Dim LastRowStitch As Long 'Lastrow of the applicable output sheet
Dim Yr As Integer
Dim Mnth As Integer
LastRowMain = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row '473,379 rows
j = 3 'First data column in the table
Do Until j = 14
For i = 4 To LastRowMain
'Sheet1 column(1) is Date format in 1/2 hour intervasls, i.e. 48 rows per day
Yr = Year(Sheet1.Cells(i, 1))
Mnth = Month(Sheet1.Cells(i, 1))
If Yr = Sheet3.Cells(2, j) And Mnth = Sheet3.Cells(1, j) Then 'If the Date in Sheet1.column(1) matches the date and month in the table, Then
LastRowStitch = Sheet2.Cells(Rows.Count, 1).End(xlUp).Row
Sheet1.Range(Sheet1.Cells(i, 1), Sheet1.Cells(i, 8)).Copy Sheet2.Cells(LastRowStitch + 1, 1) 'Copy that row and put in in to the output sheet below the lastrow
End If
If Yr = Sheet3.Cells(3, j) And Mnth = Sheet3.Cells(1, j) Then 'If the Date in Sheet1.column(1) matches the date and month in the table, Then
LastRowStitch = Sheet4.Cells(Rows.Count, 1).End(xlUp).Row
Sheet1.Range(Sheet1.Cells(i, 1), Sheet1.Cells(i, 8)).Copy Sheet4.Cells(LastRowStitch + 1, 1) 'Copy that row and put in in to the output sheet below the lastrow
End If
If Yr = Sheet3.Cells(4, j) And Mnth = Sheet3.Cells(1, j) Then 'If the Date in Sheet1.column(1) matches the date and month in the table, Then
LastRowStitch = Sheet5.Cells(Rows.Count, 1).End(xlUp).Row
Sheet1.Range(Sheet1.Cells(i, 1), Sheet1.Cells(i, 8)).Copy Sheet5.Cells(LastRowStitch + 1, 1) 'Copy that row and put in in to the output sheet below the lastrow
End If
Next i
Loop 'Go to the next set of dates in the table and loop through the rows again
Application.ScreenUpdating = True
MsgBox "Done"
End Sub
Many thanks

Refactoring a bit:
Dim YrToCompare2 As Long, YrToCompare4 As Long, YrToCompare5 As Long
Dim MnthToCompare as Long
Dim LastRow2 As Long, LastRow4 As Long, LastRow5 As Long
LastRow2 = Sheet2.Cells(Rows.Count, 1).End(xlUp).Row
LastRow4 = Sheet4.Cells(Rows.Count, 1).End(xlUp).Row
LastRow5 = Sheet5.Cells(Rows.Count, 1).End(xlUp).Row
Do Until j = 14
MnthToCompare = Sheet3.Cells(1, j)
YrToCompare2 = Sheet3.Cells(2, j)
YrToCompare4 = Sheet3.Cells(3, j)
YrToCompare5 = Sheet3.Cells(4, j)
For i = 4 To LastRowMain
'Sheet1 column(1) is Date format in 1/2 hour intervasls, i.e. 48 rows per day
Yr = Year(Sheet1.Cells(i, 1))
Mnth = Month(Sheet1.Cells(i, 1))
If Yr = YrToCompare2 And Mnth = MnthToCompare Then 'If the Date in Sheet1.column(1) matches the date and month in the table, Then
LastRow2 = LastRow2 + 1
Sheet1.Range(Sheet1.Cells(i, 1), Sheet1.Cells(i, 8)).Copy Sheet2.Cells(LastRow2, 1) 'Copy that row and put in in to the output sheet below the lastrow
End If
If Yr = YrToCompare4 And Mnth = MnthToCompare Then 'If the Date in Sheet1.column(1) matches the date and month in the table, Then
LastRow4 = LastRow4 + 1
Sheet1.Range(Sheet1.Cells(i, 1), Sheet1.Cells(i, 8)).Copy Sheet4.Cells(LastRow4, 1) 'Copy that row and put in in to the output sheet below the lastrow
End If
If Yr = YrToCompare5 And Mnth = MnthToCompare Then 'If the Date in Sheet1.column(1) matches the date and month in the table, Then
LastRow5 = LastRow5 + 1
Sheet1.Range(Sheet1.Cells(i, 1), Sheet1.Cells(i, 8)).Copy Sheet5.Cells(LastRow5, 1) 'Copy that row and put in in to the output sheet below the lastrow
End If
Next i
j = j + 1
Loop 'Go to the next set of dates in the table and loop through the rows again
The concept is to reduce VBA - Excel interactions by storing relatively fixed values in variables instead of reading them from Excel 12*500K times, and also counting last rows instead of finding them in every loop. However, you can expect significant improvement by implementing #QHarr's advise on using arrays.

Aside from ScreenUpdating and EnableEvents, you can also set the Calculation to manual before running your code. Normally, Excel will automatically recalculate a cell or a range of cells when that cell's or range's precedents have changed.
Application.Calculation = xlCalculationManual
Then once the loop is done, turn it on again:
Calculate
Application.Calculation = xlAutomatic

Related

How to set zero in the cells if the cells are empty using VBA

I have an excel file with 3 sheets inside:
Starting Date and Ending Date in Sheet1 (1st screenshot)
A database in Sheet2 (2nd screenshot)
Filtered data in Sheet3 based on the date in Sheet1 (3rd screenshot)
I have a script that copy the data from Sheet2 to Sheet3 based on the date in Sheet1. I want the script to be able to put a zero in the empty space if it is detected instead of leaving it as blank.
For example, in the 2nd screenshot above, there is no value in Row 8 for Group 4 and Group 6, so I want the script to be able to put a zero in Group 4 and Group 6 for Row 8 when the data is copied to Sheet3.
I managed to achieve this but some how there will be zero in Row 9 as well (shown in the 3rd screenshot). There should be no zero in Row 9 since the date in Sheet1 is only from December-20 to February-21 (3 months). May I know how should I modified my script so that I'm able to eliminate the zero in Row 9? Any help will be greatly appreciated!
This is my script:
Sub extractdata()
Dim LastRow As Long, erow As Long, i As Long, j As Long, LastRow2 As Long
Dim mydate As Date
LastRow2 = Sheet3.Cells(Rows.Count, 2).End(xlUp).Offset(1, 0).Row
Sheet3.Range("B6:H" & LastRow2).ClearContents
LastRow = Sheet2.Cells(Rows.Count, 2).End(xlUp).Row
For i = 6 To LastRow
mydate = Sheet2.Cells(i, 2)
If mydate >= Sheet1.Cells(7, 19) And mydate <= Sheet1.Cells(8, 19) Then
erow = Sheet3.Cells(Rows.Count, 2).End(xlUp).Offset(1, 0).Row
Sheet2.Range(Sheet2.Cells(i, 2), Sheet2.Cells(i, 57)).Copy Destination:=Sheet3.Cells(erow, 2)
End If
For j = 3 To 8
If IsEmpty(Sheet3.Cells(i, j).Value) = True Then
Sheet3.Cells(i, j) = 0
End If
Next j
Next i
End Sub
Edit
Sub extractdata()
Dim LastRow As Long, erow As Long, i As Long, j As Long, LastRow2 As Long
Dim mydate As Date
LastRow2 = Sheet3.Cells(Rows.Count, 2).End(xlUp).Offset(1, 0).Row
Sheet3.Range("B6:H" & LastRow2).ClearContents
LastRow = Sheet2.Cells(Rows.Count, 2).End(xlUp).Row
For i = 6 To LastRow
mydate = Sheet2.Cells(i, 2)
If mydate >= Sheet1.Cells(7, 19) And mydate <= Sheet1.Cells(8, 19) Then
erow = Sheet3.Cells(Rows.Count, 2).End(xlUp).Offset(1, 0).Row
Sheet2.Range(Sheet2.Cells(i, 2), Sheet2.Cells(i, 57)).Copy Destination:=Sheet3.Cells(erow, 2)
End If
Next i
ZeroIsteadBlank
End Sub
I call the function after For loop, but this is what I get:
Row 9 still exist with bunch of zero with additional date in Cell B9
Please, use this code at the end. Iterating during the copying code is waste of time and Excel resources:
Sub ZeroIsnteadBlank()
Dim sh As Worksheet, rngEmpty As Range, LastRow2 As Long
Set sh = Sheet3
LastRow2 = sh.Cells(Rows.Count, 2).End(xlUp).Row + 1 '+ 1 for an extra row...
On Error Resume Next 'Otherwise, the following line will return an error in case of no empty cells...
Set rngEmpty = sh.Range("C6:H" & LastRow2).SpecialCells(xlCellTypeBlanks)
If Not rngEmpty Is Nothing Then
rngEmpty.value = 0
Else
MsgBox "No empty cells..."
End If
On Error GoTo 0
End Sub
In order to avoid the case of an empty cell of the column C:C (C8 in your example), it should be good to calculate LastRow2 for two columns and choose the bigger one...

VBA to loop through range, if match: append part of row and specific column header to table in new sheet

I have a sheet with roughly 12000 rows and 200 columns built up in a way that doesn't allow using it as a proper database. The first 8 columns have data I need, the last 180 columns have "address" headers and an "x" for rows where the column applies, "x" can appear in a row between 1 and 46 times.
Source table format:
I want to loop through each row (only for the last 180 columns) and if a cell contains an "x" then copy values and append to a table in a new sheet:
the first 8 cells from that row
the header of the column marked by the "x", the header becomes cell 9
if there is more than 1 "x" in a row, output should have a new line for every "x" with the a copy of the first 8 cells and the corresponding header in cell 9 [edit: added 3. for clarification]
if there is no "x" in a row, that row can be ignored. The next available row in the output table should be populated with the data from the next source row that does have an "x". [edit 2: added 4. for clarification]
Result should look something like this:
I'm no VBA expert and most rows just have 1 "x" so I started with using a formula to populate column 9 with the header of the column marked by "x":
=INDEX(R3C13:R3C192, SUMPRODUCT(MAX((RC[-184]:RC[-5]=R2C198)*(COLUMN(RC[-184]:RC[-5]))))-COLUMN(R[-1]C[-184])+1)
This gives me output for every first "x" on a row, but that leaves a couple of thousand rows with between 2 and 46 times "x".
I tried getting started on this with:
Sub Test()
Dim rw As Long, Cell As Range
For Each Cell In Sheets("1").Range("K:R")
rw = Cell.Row
If Cell.Value = "x" Then
Cell.EntireRow.Copy
Sheets("2").Range("A" & rw).PasteSpecial xlPasteValues
End If
Next
End Sub
Obviously this is a pretty rough start and does not give me:
just copy the first 8 cells of the row
copy the header of the "x" column to cell 9 (for the right row)
It also does not append a new line for each "x" at the bottom of my new table.
I found some answers that are somewhat similar, such as:
Loop through rows and columns Excel Macro VBA
But have not been able to make this work for my scenario. Any help would be much appreciated, thanks!
Try this code, this sets the first 8 cells to only the rows that contain "x".
Sub appendit()
Dim i, j, lrow, lcol As Long
Dim rCount, cCount As Long
Dim addressString As String
Dim wb As Workbook
Dim ws As Worksheet
Dim newWs As Worksheet
Dim vMain As Variant
Set wb = ActiveWorkbook 'or whatever your workbook is
Set ws = wb.Sheets(1) 'or whatever your sheet is
wb.Sheets.Add(before:=wb.Sheets(1)).Name = "Output"
Set newWs = wb.Sheets("Output")
rCount = 1
With ws
lrow = .Cells(.Rows.Count, 1).End(xlUp).Row 'Load the data into an array for efficiency
lcol = .Cells(1, .Columns.Count).End(xlToLeft).Column
ReDim vMain(1 To lrow, 1 To lcol)
For i = 1 To lrow
For j = 1 To lcol
vMain(i, j) = .Cells(i, j)
Next j
Next i
End With
With newWs
For i = 21 To UBound(vMain, 2) 'starting from the 21st column as the first 20 are not to be included.
For j = 1 To UBound(vMain, 1)
If vMain(j, i) = "x" Then
.Cells(rCount, 1) = vMain(j, 1)
.Cells(rCount, 2) = vMain(j, 2)
.Cells(rCount, 3) = vMain(j, 3)
.Cells(rCount, 4) = vMain(j, 4)
.Cells(rCount, 5) = vMain(j, 5)
.Cells(rCount, 6) = vMain(j, 6)
.Cells(rCount, 7) = vMain(j, 7)
.Cells(rCount, 8) = vMain(j, 8)
.Cells(rCount, 9) = vMain(1, i)
rCount = rCount + 1
End If
Next j
Next i
End With
End Sub

Need help to optimize the Excel VBA code that aggregates duplicates

Below is my source table
Name Sales
---------------------------------
Thomas 100
Jay 200
Thomas 100
Mathew 50
Output I need is as below
Name Sales
---------------------------------
Thomas 200
Jay 200
Mathew 50
Basically, I have 2 columns that can have duplicates and I need to aggregate the second column based on first column.
Current code I have is as below. Its working perfectly fine. It takes around 45 seconds to run for 4500 records. I was wondering if there is a more efficient way to do this... as it seems to be a trivial requirement.
'Combine duplicate rows and sum values
Dim Rng As Range
Dim LngRow As Long, i As Long
LngLastRow = lRow 'The last row is calculated somewhere above...
'Initializing the first row
i = 1
'Looping until blank cell is encountered in first column
While Not Cells(i, 1).Value = ""
'Initializing range object
Set Rng = Cells(i, 1)
'Looping from last row to specified first row
For LngRow = LngLastRow To (i + 1) Step -1
'Checking whether value in the cell is equal to specified cell
If Cells(LngRow, 1).Value = Rng.Value Then
Rng.Offset(0, 1).Value = Rng.Offset(0, 1).Value + Cells(LngRow, 2).Value
Rows(LngRow).Delete
End If
Next LngRow
i = i + 1
Wend
Note that this is part of a larger excel app and hence I definitely need the solution to be in Excel VBA.
Here you go:
Option Explicit
Sub Consolidate()
Dim arrData As Variant
Dim i As Long
Dim Sales As New Scripting.Dictionary 'You will need the library Microsoft Scripting Runtime
Application.ScreenUpdating = False 'speed up the code since excel won't show you what is happening
'First of all, working on arrays always speeds up a lot the code because you are working on memory
'instead of working with the sheets
With ThisWorkbook.Sheets("YourSheet") 'change this
i = .Cells(.Rows.Count, 1).End(xlUp).Row 'last row on column A
arrData = .Range("A2", .Cells(i, 2)).Value 'here im assuming your row 1 has headers and we are storing the data into an array
End With
'Then we create a dictionary with the data
For i = 1 To UBound(arrData) 'from row 2 to the last on Q1 (the highest)
If Not Sales.Exists(arrData(i, 1)) Then
Sales.Add arrData(i, 1), arrData(i, 2) 'We add the worker(Key) with his sales(Item)
Else
Sales(arrData(i, 1)) = Sales(arrData(i, 1)) + arrData(i, 2) 'if the worker already exists, sum his sales
End If
Next i
'Now you have all the workers just once
'If you want to delete column A and B and just leave the consolidate data:
With ThisWorkbook.Sheets("YourSheet") 'change this
i = .Cells(.Rows.Count, 1).End(xlUp).Row 'last row on column A
.Range("A2:B" & i).ClearContents
.Cells(2, 1).Resize(Sales.Count) = Application.Transpose(Sales.Keys) 'workers
.Cells(2, 2).Resize(Sales.Count) = Application.Transpose(Sales.Items) 'Their sales
End With
Application.ScreenUpdating = True 'return excel to normal
End Sub
To learn everything about dictionaries (and more) check this
With data in cols A and B like:
Running this short macro:
Sub KopyII()
Dim cell As Range, N As Long
Columns("A:A").Copy Range("C1")
ActiveSheet.Range("C:C").RemoveDuplicates Columns:=1, Header:=xlNo
N = Cells(Rows.Count, "C").End(xlUp).Row
Range("B1").Copy Range("D1")
Range("D2:D" & N).Formula = "=SUMPRODUCT(--(A:A= C2),(B:B))"
End Sub
will produce this in cols C and D:
NOTE:
This relies on Excel's builtin RemoveDuplicates feature.
EDIT#1:
As chris neilsen points out, this function should be a bit quicker to evaluate:
Sub KopyIII()
Dim cell As Range, N As Long, A As Range, C As Range
Set A = Range("A:A")
Set C = Range("C:C")
A.Copy C
C.RemoveDuplicates Columns:=1, Header:=xlNo
N = Cells(Rows.Count, "C").End(xlUp).Row
Range("B1").Copy Range("D1") ' the header
Range("D2:D" & N).Formula = "=SUMIFS(B:B,A:A,C2)"
End Sub

VBA Remove duplicates taking 30 minutes to run with no idea why

So the code below copies data from two columns in one sheet. Pastes these so that values are displayed in another sheet (because one column is a formula =Left(Column+1,4)) and then attempts to run a remove duplicates across the two columns that are pasted.
This takes roughly 30 minutes to run on what is essentially 100k cells (2 columns of 50k rows each).
This is what I've been using
Sub ProjTrending1()
Dim s1 As Worksheet, s2 As Worksheet
Dim St As Date, Et As Date
Dim Tt As Double
St = Time
Application.ScreenUpdating = False
'Defines S1 as a Worksheet
Set s1 = Sheets("All Data")
'Defines S2 as WorkSheet
Set s2 = Sheets("Workings")
'Defines LastR1
Dim LR1 As Long
Dim LR2 As Long
'Finds last row cell working sheet
LR2 = s1.Cells(Rows.Count, 10).End(xlUp).Row
'Takes Data from Order Column of defined data Sheet and copy & pastes it to Working Sheet Column B
s1.Range("J1:J" & LR2).Copy s2.Range("A1")
s1.Range("e1:e" & LR2).Copy
s2.Range("b1").PasteSpecial Paste:=xlPasteValues
LR1 = s2.Range("A1").CurrentRegion.Rows.Count
'Removes Duplicates from Column B Working sheet
s2.Range("A2:B" & LR1).RemoveDuplicates Columns:=Array(1, 2), Header:=xlNo
'Copies the formula from C2 and applies it to all cells in column C where column A has values (simple concatenate + countifs(B$2:B2,B2)
s2.Range("C2").Copy s2.Range("C2:C" & LR1)
Et = Time
Tt = (Et - St) * 24 * 60 * 60
MsgBox Timetaken
End Sub
I've also tried using a dictionary to do this but I'm new to dictionaries so whilst the code looks good compared to my usual attempts its because its taken from a couple of different sources. (Copied and Pasted the data to sheet2 incase this overwrote the source data)
Sub M_delete_duplicates()
sn = Sheets("Sheet2").Cells(1).CurrentRegion.Resize(, 5)
With CreateObject("scripting.dictionary")
For j = 1 To UBound(sn)
.Item(sn(j, 1)) = Application.Index(sn, j, 0)
Next
Sheets("Sheet2").Cells(1, 4).Resize(.Count, UBound(sn, 2)) = Application.Index(.Items, 0, 0)
End With
End Sub
This is as slow to run and it only does remove duplicates based on single column and I need it to operate on two columns. The potential way around this is to concatenate the two columns of data and run the remove duplicates once and then break the data using =right(Value,X)
If wanted to do it manually it takes 30 seconds max. It makes no sense to me as to why it takes so long to run.
Can anyone help with why this might be taking so long to run? and how I might modify the dictionary code to remove duplicates over two columns?
Thanks in advance
Updated from my comment. This uses a dictionary to track which rows have been added and then copies unique rows across to the destination sheet. You may want to modify it a bit for your use (e.g. update sheet names) Always test this first on a copy of your data set or make a back up before running code
Option Explicit
Public Sub ExampleRemoveDuplicates()
Dim dict As Object
Dim temp As String
Dim calc As String
Dim headers As Variant
Dim NoCol As Long, NoRow As Long, i As Long, j As Long
Dim c, key
With Application
.ScreenUpdating = False
calc = .Calculation
.Calculation = xlCalculationManual
End With
Set dict = CreateObject("Scripting.Dictionary")
' Change this to the sheet that is applicable
With Sheet1
NoCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
' Assumes first row of sheet is headers
headers = .Range(.Cells(1, 1), .Cells(1, NoCol)).Value2
' Change this to destination sheet
With Sheet2
.Cells.Clear
.Range(.Cells(1, 1), .Cells(1, NoCol)).Value2 = headers
End With
For Each c In .Range(.Cells(2, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1))
ReDim arr(1 To NoCol)
j = 1
Do
arr(j) = c.Offset(0, j - 1).Value2
j = j + 1
Loop Until j = NoCol + 1
temp = Join(arr, "//")
If Not dict.exists(temp) And Not temp = vbNullString Then
dict.Add key:=temp, Item:=arr
' Change this to destination sheet
With Sheet2
NoRow = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
.Range(.Cells(NoRow, 1), .Cells(NoRow, NoCol)).Value2 = arr
End With
End If
Next c
End With
i = 1
ReDim Results(1 To dict.Count, 1 To NoCol)
For Each key In dict.keys
For j = 1 To NoCol
Results(i, j) = dict(key)(j)
Next j
i = i + 1
Next key
' Change this to destination sheet
With Sheet2.Cells(1, 1)
.Range(.Offset(1, 0), .Offset(dict.Count, NoCol - 1)) = Results
End With
With Application
.Calculation = calc
.ScreenUpdating = True
End With
End Sub

Dynamically reformatting an excel sheet

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

Resources