Cut and paste dynamic ranges of data repeatedly - excel

I am a PhD student and new to VBA.
I have been trying to automate sorting 1,8 million data points into a format suitable for data analysis. I am stuck. Would really appreciate anyone's help!
I need to automate the following:
Cutting a range of data in x sequential rows with identical Sequence numbers in column B.
And then paste it onto the right next to the previous range.
I found this code Excel VBA cut and paste rage repeatedly
Sub Cutrange()
Dim i As Long
Dim Lrow As Long
Lrow = Cells(Rows.Count, 1).End(xlUp).Row
Dim oRange As Range, dRange As Range
Set oRange = Range(Cells(1, 1), Cells(124, 14))
Set dRange = Cells(1, 1)
For i = 1 To Lrow
oRange.Offset(124 * i).Cut Destination:=dRange.Offset(, 14 * i)
Next i
End Sub
However, this code cuts and pastes a pre-determined range of cells (124x14). In my data, the number of rows (to be cut and pasted) needs to change based on how many rows have the same Sequence number.
Could anyone kindly help to change this code to fit my purpose or suggest alternative solutions?
Thank you,
Anna

HStack Groups of Data (VBA)
Option Explicit
Sub HStackGroups()
Const SRC_NAME As String = "Sheet1"
Const SRC_FIRST_CELL As String = "B2"
Const DST_NAME As String = "Sheet2"
Const DST_FIRST_CELL As String = "B2"
Const UNIQUE_COLUMN As Long = 1
Const COLUMN_GAP As Long = 1
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Sheets(SRC_NAME)
Dim srg As Range: Set srg = sws.Range(SRC_FIRST_CELL).CurrentRegion
Dim hData(): hData = srg.Rows(1).Value
Dim cCount As Long: cCount = srg.Columns.Count
Dim srCount As Long: srCount = srg.Rows.Count - 1 ' no headers
Dim sData(): sData = srg.Resize(srCount).Offset(1).Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim sr As Long, drCount As Long, srString As String
For sr = 1 To srCount
srString = CStr(sData(sr, UNIQUE_COLUMN))
If Not dict.Exists(srString) Then
Set dict(srString) = New Collection
End If
dict(srString).Add sr
If dict(srString).Count > drCount Then drCount = dict(srString).Count
Next sr
drCount = drCount + 1 ' 1 for headers
Dim dCount As Long: dCount = cCount + COLUMN_GAP
Dim dcCount As Long
dcCount = dict.Count * dCount - COLUMN_GAP
Dim dData(): ReDim dData(1 To drCount, 1 To dcCount)
Dim Coll, Item, sc As Long, d As Long, dr As Long, dc As Long
For Each Coll In dict.Items
dc = d * dCount
For sc = 1 To cCount
dData(1, dc + sc) = hData(1, sc)
Next sc
dr = 1
For Each Item In Coll
dr = dr + 1
For sc = 1 To cCount
dData(dr, dc + sc) = sData(Item, sc)
Next sc
Next Item
d = d + 1
Next Coll
Dim dws As Worksheet: Set dws = wb.Sheets(DST_NAME)
Dim dfCell As Range: Set dfCell = dws.Range(DST_FIRST_CELL)
Dim drg As Range: Set drg = dfCell.Resize(drCount, dcCount)
With dfCell
.Resize(dws.Rows.Count - .Row + 1, dws.Columns.Count - .Column + 1) _
.Clear
End With
drg.Value = dData
drg.EntireColumn.AutoFit
MsgBox "Groups hstacked.", vbInformation
End Sub

Related

how to get column name if that column has value for each row in excel

The source picture has names in the 1st column and the 1st row has dated. There are values for each date column. Need to get Dates and their values for each name if there is a value for a particular date.
A Simple Unpivot
Sub UnpivotRCV()
' Define constants.
Const SRC_NAME As String = "Sheet1"
Const DST_NAME As String = "Sheet2"
Const DST_FIRST_CELL As String = "A2"
Const DST_COLUMNS_COUNT As Long = 3 ' fixed
' Reference the workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source range.
Dim sws As Worksheet: Set sws = wb.Sheets(SRC_NAME)
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion
Dim srCount As Long: srCount = srg.Rows.Count
Dim scCount As Long: scCount = srg.Columns.Count
' Write the values from the source range to the source array.
Dim sData: sData = srg.Value
' Define the destination array.
Dim drCount As Long: drCount = (srCount - 1) * (scCount - 1)
Dim dData(): ReDim dData(1 To drCount, 1 To DST_COLUMNS_COUNT)
Dim sr As Long, sc As Long, dr As Long
' Return the unpivoted values from the source array
' in the destination array.
For sr = 2 To srCount
For sc = 2 To scCount
If Len(CStr(sData(sr, sc))) > 0 Then
dr = dr + 1
dData(dr, 1) = sData(sr, 1) ' row label
dData(dr, 2) = sData(1, sc) ' column label
dData(dr, 3) = sData(sr, sc) ' value
End If
Next sc
Next sr
' Reference the destination range.
Dim dws As Worksheet: Set dws = wb.Sheets(DST_NAME)
Dim dfCell As Range: Set dfCell = dws.Range(DST_FIRST_CELL)
Dim drg As Range: Set drg = dfCell.Resize(dr, DST_COLUMNS_COUNT)
' Write, clear and autfit.
drg.Value = dData
drg.Resize(dws.Rows.Count - drg.Row - dr + 1).Offset(dr).ClearContents
drg.EntireColumn.AutoFit
MsgBox "Data unpivoted.", vbInformation
End Sub

Looping through rows in excel not sure how to assign a counter to go into different rows

Good morning,
I have a table with values that increment in the rows from left to right and then they change again as soon as I go down further
I wanted to loop through the rows and set the values in these rows in a different sheet to go in column A from row 2 and then it increments from A2 --> A3 --> A4...etc.
Sub LoopthroughRows ()
LastRow = Range("O" & Rows.Count).End(xlUp).Row
FirstRow = 2
i = FirstRow
FirstColumn = 15
Do Until i > LastRow
LastColumn = Cells(i, Columns.Count).End(xlToLeft).Column
Count = FirstColumn
k = 2
Do Until Count > LastColumn
Set Worksheets(Sheet7).Range("A" & k).Value = Worksheets(Sheet5).Range(Chr(Count + 64) & i).Value
Count = Count + 1
Loop
k=k+1
i=i+1
Loop
End Sub
when I run the code it comes up with Run time error '13' type mismatch. I tested the run through rows function and it works. I believe the issue might be with the set function in my Do loop?
Please help! I am using this to convert the rows into 1 column.
Thank you and have a great week :)
Get Column From Range
A Quick Fix: Practicing Do Loops (Slow)
Sub LoopthroughRows()
Dim fCell As Range: Set fCell = Sheet5.Range("O2")
Dim FirstRow As Long: FirstRow = fCell.Row
Dim FirstColumn As Long: FirstColumn = fCell.Column
Dim LastRow As Long
LastRow = Sheet5.Cells(Sheet5.Rows.Count, FirstColumn).End(xlUp).Row
Dim sr As Long: sr = FirstRow
Dim dr As Long: dr = 2
Dim LastColumn As Long
Dim sc As Long
Do Until sr > LastRow
sc = FirstColumn
LastColumn = Sheet5.Cells(sr, Sheet5.Columns.Count).End(xlToLeft).Column
Do Until sc > LastColumn
Sheet7.Cells(dr, "A").Value = Sheet5.Cells(sr, sc).Value
sc = sc + 1
dr = dr + 1
Loop
sr = sr + 1
Loop
End Sub
An Improvement: Using a Function (Fast)
Sub GetColumnFromRangeTEST()
Dim sfCell As Range: Set sfCell = Sheet5.Range("O2")
Dim srg As Range
With sfCell.CurrentRegion
Set srg = sfCell.Resize(.Row + .Rows.Count - sfCell.Row, _
.Column + .Columns.Count - sfCell.Column)
End With
Dim Data() As Variant
' Read by rows:
Data = GetColumnFromRange(srg)
' Read by columns:
'Data = GetColumnFromRange(srg, True)
Dim dfCell As Range: Set dfCell = Sheet7.Range("A2")
Dim drg As Range: Set drg = dfCell.Resize(UBound(Data, 1))
drg.Value = Data
End Sub
Function GetColumnFromRange( _
ByVal rg As Range, _
Optional ByVal ReadByColumns As Boolean = False) _
As Variant()
Dim srCount As Long: srCount = rg.Rows.Count
Dim scCount As Long: scCount = rg.Columns.Count
Dim drCount As Long: drCount = srCount * scCount
Dim sData() As Variant
If drCount = 1 Then
ReDim sData(1 To 1, 1 To 1): sData(1, 1) = rg.Value
Else
sData = rg.Value
End If
Dim dData() As Variant: ReDim dData(1 To drCount, 1 To 1)
Dim sr As Long, sc As Long, dr As Long
If ReadByColumns Then
For sc = 1 To scCount
For sr = 1 To srCount
dr = dr + 1
dData(dr, 1) = sData(sr, sc)
Next sr
Next sc
Else
For sr = 1 To srCount
For sc = 1 To scCount
dr = dr + 1
dData(dr, 1) = sData(sr, sc)
Next sc
Next sr
End If
GetColumnFromRange = dData
End Function
If this is a simply swap of rows/columns, you can do this without looping:
Sub test()
With Sheets(1)
Dim sourceRng As Range: Set sourceRng = .Range(.Cells(1, 1), .Cells(4, 2))
.Cells(6, 6).Resize(sourceRng.Columns.Count, sourceRng.Rows.Count).Value = Application.Transpose(sourceRng)
End With
End Sub
Note that I use sourceRng.Columns.Count in the "row" place and sourceRng.Rows.Count in the "column" place for the resize.
Edit1:
Modifying to indicate how to utilize as a loop (untested):
Sub test()
With Sheets(1)
Dim i as Long
For i = firstRowSource to lastRowSource
Dim sourceRng As Range: Set sourceRng = .Range(.Cells(i, 1), .Cells(i, 2))
Dim targetColDest as Long: targetColDest = targetColDest + 1
.Cells(1, targetColDest ).Resize(sourceRng.Columns.Count,).Value = Application.Transpose(sourceRng)
Next i
End With
End Sub
This code converts rows to the one long column (values from 0 to 319)
Sub LoopthroughRows()
With ThisWorkbook
a = .Sheets(1).Range("O2").CurrentRegion
ReDim b(UBound(a, 1) * UBound(a, 2))
i = 0
For r = 1 To UBound(a, 1)
For c = 1 To UBound(a, 2)
b(i) = a(r, c)
i = i + 1
Next
Next
.Sheets(2).Range("A2").Resize(UBound(b)) = WorksheetFunction.Transpose(b)
End With
End Sub

VBA code to copy and paste rows three times from one worksheet to another

My idea is to get the data that I have on "A4" ("Data" worksheet) and paste it 4 times on "B4" ("Forecast" worksheet). After that, take the data from "A5" and do the same (starting from the first blank cell) until there is no more data on the "A" column. After there is no more data, the process should stop.
How do I tell excel to paste the value in the first blank cell in column B ("Forecast" worksheet)?
Sub Test()
Dim wsSource As Worksheet, wsTarget As Worksheet
Dim i As Integer, k As Integer
Set wsSource = ThisWorkbook.Worksheets("Data")
Set wsTarget = ThisWorkbook.Worksheets("Forecast")
k = wsSource.Range("A4", wsSource.Range("A4").End(xlDown)).Rows.Count
For i = 1 To k
wsTarget.Range("B4", "B7").Value = wsSource.Range("A" & 3 + i).Value
Next
End Sub
Copy Repetitions
A Quick Fix
Sub Test()
Dim wsSource As Worksheet, wsTarget As Worksheet
Dim sCell As Range, tCell As Range
Dim i As Long, j As Long, k As Long
Set wsSource = ThisWorkbook.Worksheets("Data")
Set wsTarget = ThisWorkbook.Worksheets("Forecast")
k = wsSource.Range("A4", wsSource.Range("A4").End(xlDown)).Rows.Count
Set sCell = wsSource.Range("A4")
Set tCell = wsTarget.Range("B4")
For i = 1 To k
For j = 1 To 4
tCell.Value = sCell.Value
Set tCell = tCell.Offset(1)
Next j
Set sCell = sCell.Offset(1)
Next i
End Sub
My Choice
Sub CopyRepetitions()
' Source
Const sName As String = "Data"
Const sfCellAddress As String = "A4"
' Destination
Const dName As String = "Forecast"
Const dfCellAddress As String = "B4"
Const Repetitions As Long = 4
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source (one-column) range ('srg').
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim srg As Range
Dim srCount As Long
With sws.Range(sfCellAddress)
Dim lCell As Range: Set lCell = .Resize(sws.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Sub ' no data
srCount = lCell.Row - .Row + 1
Set srg = .Resize(srCount)
End With
' Write values from the source range the source array ('sData')
Dim sData As Variant
If srCount = 1 Then
ReDim sData(1 To 1, 1 To 1): sData(1, 1) = srg.Value
Else
sData = srg.Value
End If
' Define the destination array ('dData').
Dim drCount As Long: drCount = srCount * Repetitions
Dim dData As Variant: ReDim dData(1 To drCount, 1 To 1)
' Write the repeating values from the source- to the destination array.
Dim sr As Long
Dim rep As Long
Dim dr As Long
For sr = 1 To srCount
For rep = 1 To Repetitions
dr = dr + 1
dData(dr, 1) = sData(sr, 1)
Next rep
Next sr
' Write the values from the destination array to the destination
' one-column range and clear the data below.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
With dws.Range(dfCellAddress)
.Resize(drCount).Value = dData
.Resize(dws.Rows.Count - .Row - drCount + 1).Offset(drCount).Clear
End With
' Inform.
MsgBox "Repetitions copied.", vbInformation
End Sub

Comparing Three Columns and Copy Pasting data

I am using below code which is comparing three columns values and copy pasting the 4th column data into other column.
My code is working fine but it is slow to perform the processing and takes much time and sometimes Not Responding window appears.
Any help to fix the problem will be appreciated
Sub rowMatch()
Dim ws As Worksheet
Dim ws2 As Worksheet
Set ws = Worksheets("Sheet3")
Set ws2 = Worksheets("Sheet2")
Dim a As String, b As String, c As Date
For i = 3 To ws.Cells(ws.Rows.Count, 14).End(xlUp).Row
a = ws.Cells(i, 14).Value
b = ws.Cells(i, 15).Value
c = ws.Cells(i, 16).Value
For j = 3 To ws2.Cells(ws2.Rows.Count, 98).End(xlUp).Row
If ws2.Cells(j, 98).Value = a _
And ws2.Cells(j, 103).Value = b _
And ws2.Cells(j, 114).Value = c _
Then
ws2.Cells(j, 120).Value = ws.Cells(j, 18).Value
End If
Next j
Next i
End Sub
A 'Triple' Lookup
When in a loop a condition 1. is not met or 2. is met, use Exit For to stop looping (to exit the loop).
Option Explicit
Sub TripleLookup()
' Source
Const sName As String = "Sheet3" ' Worksheet Name
Const sfRow As Long = 3 ' First Row
Const slColsList As String = "N,O,P" ' Lookup Columns
Const slrCol As String = "N" ' Last Row Column
Const svCol As String = "R" ' Value Column
' Destination
Const dName As String = "Sheet2" ' Worksheet Name
Const dfRow As Long = 3 ' First Row
Const dlColsList As String = "CT,CY,DJ" ' Lookup Columns
Const dlrcol As String = "CT" ' Last Row Column
Const dvCol As String = "DP" ' Value Column
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, slrCol).End(xlUp).Row
If slRow < sfRow Then Exit Sub ' no data
Dim srCount As Long: srCount = slRow - sfRow + 1
Dim slrrg As Range: Set slrrg = sws.Cells(sfRow, slrCol).Resize(srCount)
Dim slCols() As String: slCols = Split(slColsList, ",")
Dim cUpper As Long: cUpper = UBound(slCols)
Dim sData As Variant: ReDim sData(0 To cUpper)
Dim srg As Range
Dim n As Long
For n = 0 To cUpper
Set srg = slrrg.EntireRow.Columns(slCols(n))
sData(n) = srg.Value
Next n
Set srg = slrrg.EntireRow.Columns(svCol)
Dim svData As Variant: svData = srg.Value
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, dlrcol).End(xlUp).Row
If dlRow < dfRow Then Exit Sub ' no data
Dim drCount As Long: drCount = dlRow - dfRow + 1
Dim dlrrg As Range: Set dlrrg = dws.Cells(dfRow, dlrcol).Resize(drCount)
Dim dlCols() As String: dlCols = Split(dlColsList, ",")
Dim dData As Variant: ReDim dData(0 To cUpper)
Dim drg As Range
For n = 0 To cUpper
Set drg = dlrrg.EntireRow.Columns(dlCols(n))
dData(n) = drg.Value
Next n
Dim dvData As Variant: ReDim dvData(1 To drCount, 1 To 1)
' Loop
Dim tUpper As Long: tUpper = cUpper + 1
Dim sr As Long
Dim dr As Long
For dr = 1 To drCount
For sr = 1 To srCount
For n = 0 To cUpper
If StrComp(CStr(sData(n)(sr, 1)), CStr(dData(n)(dr, 1)), _
vbTextCompare) <> 0 Then ' ignoring case
Exit For ' 1. mismatch found, no need to loop anymore
End If
Next n
If n = tUpper Then ' no mismatch, all are equal
dvData(dr, 1) = svData(sr, 1)
Exit For ' 2. value written, no need to loop anymore
End If
Next sr
Next dr
' Result
Set drg = dlrrg.EntireRow.Columns(dvCol)
drg.Value = dvData
End Sub
Looking at the code you have two nested For loops, which depending on the size of the dataset you're working with, it will likely continue to be an issue on the time it takes to complete the task. The more rows you have, the longer it will take to complete the tasks.
One possible solution that you might want to consider looking into is using power query to blend/merge/append and clean data. Then use VBA to do what Power Query doesn't do very well.
I've spent many years avoiding looking at PQ as a solution to these problems and was a VBA guy all the way, but now when I come across these types of problems,I would look at PQ first over VBA.

Excel get value based on multiple column condition

Below is the table I have
Client Accuracy Utilization TAT Volume
ABC 1 2 3 4
XYZ 5 2 4 3
PQR 2 2 5 2
The output should be something like below
Client Key Indicator
ABC Accuracy
ABC Utilization
XYZ Utilization
PQR Accuracy
PQR Utilization
PQR Volume
So for ratings less than 3 the client name and key indicator that has value less than 3 must get populated.
I tried using vlookup but the result is not as expected
Any insights how to achieve this.
If one has Office 365 we can do:
=LET(
clt,$A$2:$A$4,
ind,$B$1:$E$1,
rng,B2:E4,
sq,COLUMNS(rng)*ROWS(rng),
md,MOD(SEQUENCE(sq,,0),COLUMNS(rng))+1,
it,INT(SEQUENCE(sq,,1,1/COLUMNS(rng))),
FILTER(CHOOSE({1,2},INDEX(clt,it),INDEX(ind,,md)),INDEX(rng,it,md)<3,"")
)
Without Office 365, PowerQuery or VBA will be the best to normalize and filter the data.
Another Unpivot Flavor
Option Explicit
Sub UnPivot()
Const sName As String = "Sheet1"
Const sCriteria As Long = 3
Const sOperator As String = "<"
Const dName As String = "Sheet1"
Const dFirst As String = "G1"
Const dHeader As String = "Key Indicator"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Create a reference to the Source Range ('srg').
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion
' Validate the Source Rows Count ('srCount')
' and Columns Count ('scCount').
Dim srCount As Long: srCount = srg.Rows.Count
If srCount < 2 Then Exit Sub ' only column labels, no data
Dim scCount As Long: scCount = srg.Columns.Count
If scCount < 2 Then Exit Sub ' only row labels, no data
' Define the Source Array ('sData').
Dim sData As Variant: sData = srg.Value
' Create a reference to the Source Values Range ('svrg').
Dim svrg As Range
Set svrg = srg.Resize(srCount - 1, scCount - 1).Offset(1, 1)
' Calculate the Destination Rows Count ('drCount').
Dim drCount As Long
drCount = Application.CountIf(svrg, sOperator & CStr(sCriteria)) + 1
If drCount = 1 Then Exit Sub
' Define the Destination Array ('dData').
Dim dData As Variant: ReDim dData(1 To drCount, 1 To 2)
' Write headers to the Destination Array.
dData(1, 1) = sData(1, 1)
dData(1, 2) = dHeader
' Declare variables.
Dim r As Long
Dim c As Long
Dim n As Long
' Write the data (row labels, column labels) to the Destination Array.
n = 1 ' because of headers
For r = 2 To srCount
For c = 2 To scCount
If sData(r, c) < sCriteria Then
n = n + 1
dData(n, 1) = sData(r, 1)
dData(n, 2) = sData(1, c)
End If
Next c
Next r
' Write the values from the Destination Array
' to the Destination Range ('drg').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dFirst)
Dim drg As Range: Set drg = dfCell.Resize(drCount, 2)
drg.Value = dData
' Clear the contents of the Clear Range ('crg'), the range
' below the Destination Range.
Dim crg As Range
Set crg = drg.Resize(dws.Rows.Count - drg.Row - drCount + 1) _
.Offset(drCount)
crg.ClearContents
' Autofit the (entire) columns of the Destination Range.
drg.EntireColumn.AutoFit
' Save the changes.
wb.Save
End Sub

Resources