change to dynamic vba - excel

i want to have rows from one worsheet copy to another worksheet based on a specific text, i need it to run as new data will be added daily, i am using this code now but it needs to be run after inputting data.
Sub CopyYes()
Dim c As Range
Dim j As Integer
Dim Source As Worksheet
Dim Target As Worksheet
Set Source = ActiveWorkbook.Worksheets("MAINGANG")
Set Target = ActiveWorkbook.Worksheets("REPAIRS")
j = 4
For Each c In Source.Range("C4:C10000")
If c = "X" Then
Source.Rows(c.Row).Copy Target.Rows(j)
j = j + 1
End If
Next c
End Sub

I think this will achieve what you are looking for:
Sub CopyYes()
Dim myCell As Range
Dim LastColumnSource As Long 'Integer data type is outdated.
Dim LastRowTarget As Long
Dim SourceSheet As Worksheet
Dim TargetSheet As Worksheet
Dim SourceRange As Range
Dim TargetRange As Range
Dim myArray As Variant
Set SourceSheet = ActiveWorkbook.Worksheets("Sheet1") <~~ change to your sheet name
Set TargetSheet = ActiveWorkbook.Worksheets("Sheet2") <~~ change to your sheet name
'Change the 1 to whichever column you need (1 represents column A)
LastRowTarget = TargetSheet.Cells(Rows.Count, 1).End(xlUp).Row
LastRowSource = SourceSheet.Cells(Rows.Count, 3).End(xlUp).Row
Set SourceRange = SourceSheet.Range("C4:C" & LastRowSource)
j = 4
For Each myCell In SourceRange
If myCell.Value = "X" Then
LastColumnSource = SourceSheet.Cells(myCell.Row, Columns.Count).End(xlToLeft).Column
myArray = SourceSheet.Range(Cells(myCell.Row, 1), Cells(myCell.Row, LastColumnSource))
LastColumnTarget = TargetSheet.Cells(LastRowTarget, Columns.Count).End(xlToLeft).Column
Set TargetRange = TargetSheet.Range("A" & LastRowTarget)
TargetRange.Resize(1, UBound(myArray, 2)) = myArray
LastRowTarget = LastRowTarget + 1
End If
Next myCell
End Sub
To make it dynamic, the last row and last column are found for both sheets and the row is written to an array to then write back to the results sheet (which avoids the use of copy).
As I don't know enough about your project, I've left the ActiveWorkbook in but you'd be better to specify the workbook (or ThisWorkbook if it's the workbook the code is run from) - This avoids runtime errors if the code executes whilst another workbook is in focus.

Usually, I use an array to process, as follows
’--------------------------------
dim arr(),temp()
worksheets(1).activate
arr=[a1].currentregion
j=0
for i=1 to ubound(arr)
if arr(i)="x" then
j=j+1
redim preserve temp(j)
temp(j)=arr(i)
end if
next
worksheets(2).activate
range("a1:a"&ubound(temp))=temp

Try,
Sub CopyYes()
Dim Source As Worksheet
Dim Target As Worksheet
Dim vDB, vR()
Dim i As Long, n As Long, r As Long
Dim j As Integer, c As Integer
Set Source = ActiveWorkbook.Worksheets("MAINGANG")
Set Target = ActiveWorkbook.Worksheets("REPAIRS")
vDB = Source.UsedRange
r = UBound(vDB, 1)
c = UBound(vDB, 2)
For i = 4 To r
If vDB(i, 3) = "X" Then
n = n + 1
ReDim Preserve vR(1 To c, 1 To n)
For j = 1 To c
vR(j, n) = vDB(i, j)
Next j
End If
End If
Target.Range("a4").Resize(n, c) = WorksheetFunction.Transpose(vR)
End Sub

Related

Replace Headers name in the first row from the cells column of another sheet

I am trying to achieve the following automation in VBA. I have different Sheets with wrong headers. I have another sheet called "HeadersMap", which contains the list of all Sheets's correct headers. What I want to do is, if I open a "Sheet1" then the code should go to the "HeadersMap" sheet > check the opened sheet name in the "SheetNames" column > check the Original header in "OriginalHeaders" column and copy correct header name from the "Correct Headers" column and replace the headers in the "Sheet1". And similarly , if I open "Sheet2", it should do the same.
"SHEET1"
A
B
C
1
aplpe
baanann
Roange
2
3
SHEET "HEADERSMAP"
A
B
C
1
SheetNames
OriginalHeaders
CorrectHeaders
2
Sheet1
aplpe
Apple
3
Sheet1
baanann
Banana
4
Sheet1
Roange
Orange
5
Sheet2
sgura
Sugar
6
Sheet2
Jggaery
Jaggery
7
Sheet3
Dtergetn
Detergent
8
Sheet3
poas
Soap
9
Sheet3
Lfua
Lufa
Desired Result "SHEET1"
A
B
C
1
Apple
Banana
Orange
2
3
Try,
Sub test()
Dim Ws As Worksheet
Dim vDB As Variant
Dim rngHeader As Range
Dim i As Integer
Set Ws = Sheets("HEADERSMAP")
vDB = Ws.Range("a1").CurrentRegion
For i = 2 To UBound(vDB, 1)
If isHas(vDB(i, 1)) Then
Set Ws = Sheets(vDB(i, 1))
Set rngHeader = Ws.Rows(1)
rngHeader.Replace vDB(i, 2), vDB(i, 3)
End If
Next i
End Sub
Function isHas(v As Variant) As Boolean
Dim Ws As Worksheet
For Each Ws In Worksheets
If Ws.Name = v Then
isHas = True
Exit Function
End If
Next Ws
End Function
Correct Headers
Edit
After reading your comment, it may be best to copy the complete code to the ThisWorkbook module (if you insist on this functionality). There is no need for adding another module.
It is assumed that the data in worksheet HeadersMap starts in cell A1.
Standard Module e.g. Module1
Option Explicit
Sub correctHeaders(ws As Worksheet)
Const sName As String = "HeadersMap"
Const sFirst As String = "A1"
Dim rg As Range
Dim Data As Variant
Set rg = ThisWorkbook.Worksheets(sName).Range(sFirst).CurrentRegion
If IsNumeric(Application.Match(ws.Name, rg.Columns(1), 0)) Then
Data = rg.Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim Result() As Variant
Dim r As Long, j As Long
For r = 1 To UBound(Data, 1)
If StrComp(Data(r, 1), ws.Name, vbTextCompare) = 0 Then
j = j + 1
ReDim Preserve Result(1 To 2, 1 To j)
Result(1, j) = Data(r, 2)
Result(2, j) = Data(r, 3)
End If
Next r
If j > 0 Then
Set rg = ws.UsedRange.Rows(1)
Data = rg.Value
Dim cIndex As Variant
For j = 1 To j
cIndex = Application.Match(Result(1, j), Data, 0)
If IsNumeric(cIndex) Then
Data(1, cIndex) = Result(2, j)
End If
Next j
rg.Value = Data
End If
End If
End Sub
Additional Functionality (you have to run it)
Sub correctHeadersApply
Dim ws As Worksheet
For Each ws in Thisworkbook.Worksheets
correctHeaders ws
Next ws
End Sub
ThisWorkbook Module
Option Explicit
Private Sub Workbook_Open()
correctHeaders ActiveSheet
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
If Sh.Type = xlWorksheet Then
correctHeaders Sh
End If
End Sub
Bare minimum would probably be putting this in ThisWorkbook:
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Dim targetRange As Range, i As Long
Set targetRange = Worksheets("HEADERSMAP").Range("A1:A9")
i = 1
For Each entry In targetRange
If entry.Value = Sh.NAME Then
Sh.Cells(1, i) = entry.Offset(, 2).Value
i = i + 1
End If
Next
End Sub
If the data is looking like your examples.
Later you might want ot change Range("A1:A9") to look for the last row, and Offset(, 2) to maybe Offset(, 1) since the "OriginalHeaders" column is superflous in reality.
The Module version would be something like:
Sub headers()
Dim targetRange As Range, i As Long, Sh As Worksheet
Set Sh = Worksheets(InputBox("Enter name of sheet"))
Set targetRange = Worksheets("HEADERSMAP").Range("A1:A9")
i = 1
For Each entry In targetRange
If entry.Value = Sh.NAME Then
Sh.Cells(1, i) = entry.Offset(, 2).Value
i = i + 1
End If
Next
End Sub
That is if the name of the sheet and the item in the list correlate.
You could set a second variable with a second inputbox, and replace Sh.NAME to select from the list manually.
Like so:
Sub headers()
Dim targetRange As Range, i As Long, Sh As Worksheet, name As String
Set Sh = Worksheets(InputBox("Enter name of sheet"))
name = InputBox("Enter name from map")
Set targetRange = Worksheets("HEADERSMAP").Range("A1:A9")
i = 1
For Each entry In targetRange
If entry.Value = name Then
Sh.Cells(1, i) = entry.Offset(, 2).Value
i = i + 1
End If
Next
End Sub
Then you can manually type witch sheet get what headers, if you like to do that.

How to transpose specific cell values in a row based on "Y" or "N" input

The ultimate goal is to set the status of a particular row to "Yes" and have the data of that row that is highlighted RED automatically be entered into another sheet in order to be printed in a format required for a Zlabel printer.
If you can imagine this raw data on a larger scale and having to print 50+ rows daily. I do this manually now but really hoping to streamline this process
This is how I'm hoping the data will look on a separate sheet when the status is set to "Yes" regardless of how many rows there are I could print in bulk
Open to any other suggestions that may include VBA macros or any other recommended solutions.
Any advice or help is extremely appreciated!
Try,
Sub test()
Dim Ws As Worksheet, toWs As Worksheet
Dim vDB, vR()
Dim i As Long, n As Long, r As Long
Set Ws = Sheets(1) 'Data sheet
Set toWs = Sheets(2) 'Result sheet
vDB = Ws.Range("a1").CurrentRegion
r = UBound(vDB, 1)
For i = 1 To r
If vDB(i, 9) = "Yes" Then
n = n + 5
ReDim Preserve vR(1 To n)
vR(n - 4) = vDB(i, 1)
vR(n - 3) = vDB(i, 4)
vR(n - 2) = vDB(i, 5)
vR(n - 1) = vDB(i, 7)
End If
Next i
With toWs
.UsedRange = Empty
.Range("a1").Resize(n) = WorksheetFunction.Transpose(vR)
End With
End Sub
If you have Excel O365, then you could also opt for a formula. If your data has to start in Sheet2!A1 onwards then in A1:
=IF(MOD(ROW(),5)>0,INDEX(INDEX(FILTER(Sheet1!A:H,Sheet1!I:I="Yes"),SEQUENCE(COUNTIF(Sheet1!I:I,"Yes")),{1;4;5;7}),ROUNDUP(ROW()/5,0),MOD(ROW(),5)),"")
Drag down.
Copy By Criteria
The following automatically clears the contents of the Target Worksheet
("Sheet2") and copies all data specified by Crit ("Yes") to
it ("Sheet2"), when any data in the Criteria Column ("I") of
the Source Worksheet ("Sheet1") is manually changed (i.e.
it could be written to run more efficiently).
If you don't want it to run automatically, then remove the code from
the Sheet Module and just run the first Sub (maybe using a
button) when needed (which was my first idea).
You can change tgtGap, the number of rows in between data blocks.
You can add or remove columns to the Cols array.
Standard Module e.g. Module1
Option Explicit
Public Const CriteriaColumn As Variant = "I" ' e.g. "A" or 1
Sub copyByCriteria()
' Source
Const srcName As String = "Sheet1"
Const FirstRow As Long = 2
Const Crit As String = "Yes"
Dim Cols As Variant: Cols = Array("A", "D", "E", "G") ' or 1, 4, 5, 7
' Target
Const tgtName As String = "Sheet2"
Const tgtFirstCell As String = "A1"
Const tgtGap As Long = 1
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook
' Collect data from Source Worksheet.
Dim ws As Worksheet: Set ws = wb.Worksheets(srcName)
Dim Criteria As Variant
getColumn Criteria, ws, CriteriaColumn, FirstRow
If IsEmpty(Criteria) Then Exit Sub
Dim ubC As Long: ubC = UBound(Criteria)
Dim ubD As Long: ubD = UBound(Cols)
Dim Data As Variant: ReDim Data(ubD)
Dim j As Long
For j = 0 To ubD
Data(j) = ws.Cells(FirstRow, Cols(j)).Resize(ubC)
Next j
Dim critCount As Long
critCount = Application.WorksheetFunction _
.CountIf(ws.Columns(CriteriaColumn), Crit)
' Write data from Data Arrays to Target Array.
Dim Target As Variant, i As Long, k As Long
ReDim Target(1 To critCount * (ubD + 1 + tgtGap) - tgtGap, 1 To 1)
For i = 1 To ubC
If Criteria(i, 1) = Crit Then
For j = 0 To ubD
k = k + 1
Target(k, 1) = Data(j)(i, 1)
Next j
k = k + tgtGap
End If
Next i
' Write Target Array to Target Worksheet.
Set ws = wb.Worksheets(tgtName)
ws.Cells.ClearContents
ws.Range(tgtFirstCell).Resize(UBound(Target)).Value = Target
End Sub
Sub getColumn(ByRef Data As Variant, _
Sheet As Worksheet, _
Optional aColumn As Variant = 1, _
Optional FirstRow As Long = 1)
Dim rng As Range
Set rng = Sheet.Columns(aColumn).Find("*", , xlValues, , , xlPrevious)
If rng Is Nothing Then Exit Sub
If rng.Row < FirstRow Then Exit Sub
If rng.Row > FirstRow Then
Data = Sheet.Range(Sheet.Cells(FirstRow, aColumn), rng).Value
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rng.Value
End If
End Sub
Sheet Module e.g. Sheet1
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Columns(CriteriaColumn)) Is Nothing Then
copyByCriteria
End If
End Sub

Limitation for Transpose Function

I am trying to find a workaround as transpose will not fit my data size, which is giving me an error. What do I added in right before the end of the loop -before the Next- to paste the data on a new sheet? Will this slow down the macro it the output is 100,000 lines
After reviewing the code I realized that if I make the range to a certain number it works and +1 row after that it errors out. Turns out transpose is to blame.
For Q = 1 To Data + 1
n = n + 1
ReDim Preserve var(1 To 3, 1 To n)
var(1, n) =
For R = 2 To 6
var(r, n) =
Next R
var(1, n) =
var(2, n) =
Next Q
Next_Loop:
Next P
With this workbook.sheet1
If Q>= 2 Then
.Range("a1").Resize(n, 6) = WorksheetFunction.Transpose(var)
End If
The result should be instead of pasting all the data at the end, it pastes the data after each iteration (unless it slows down the macro). Next iteration would be below the previous line of data. etc.
thank you for any insight
Here is an option for you to try.
Sub LongColumnToAFewColumns()
Dim wsF As Worksheet, WST As Worksheet
Dim rf As Range, rT As Range
Dim R As Long, j As Integer
' initialize
Set wsF = ActiveSheet
Set WST = Sheets.Add
WST.Name = "Results"
j = 1
For R = 1 To wsF.Cells(Rows.Count, 1).End(xlUp).Row Step 65536
wsF.Cells(R, 1).Resize(65536).Copy
WST.Cells(j, 1).PasteSpecial xlPasteValues
WST.Cells(j, 1).PasteSpecial xlPasteValues
j = j + 1
Next R
End Sub
If you want one long column cut into a few long rows, then use this.
Sub LongColumnToAFewRows()
Dim wsF As Worksheet, WST As Worksheet
Dim rf As Range, rT As Range
Dim R As Long, j As Integer
' initialize
Set wsF = ActiveSheet
Set WST = Sheets.Add
WST.Name = "Results2"
j = 1
For R = 1 To wsF.Cells(Rows.Count, 1).End(xlUp).Row Step Columns.Count
wsF.Cells(R, 1).Resize(Columns.Count).Copy
WST.Cells(j, 1).PasteSpecial xlPasteValues, Transpose:=True
j = j + 1
Next R
End Sub
One more for consideration.
Sub testing()
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Dim ptrSource As Long
Dim ptrDest As Long
Dim colDest As Long
Set wsDest = Sheets.Add
wsDest.Name = "Results"
Set wsSource = Worksheets("Sheet1")
colDest = 1
ptrSource = 1
ptrDest = 1
Do While Len(wsSource.Cells(ptrSource, 1)) > 0
wsDest.Cells(ptrDest, colDest) = wsSource.Cells(ptrSource, 1)
If colDest = Columns.Count Then
colDest = 0
ptrDest = ptrDest + 1
End If
ptrSource = ptrSource + 1
colDest = colDest + 1
Loop
Set wsDest = Nothing
Set wsSource = Nothing
End Sub

How to make a simple "Sum" loop faster?

I am beginning to learn how to use loops and arrays but this one has me stuck. Below is a code that loops through cells and adds them together in column P.
Sub Loop_Test()
Dim sht1 As Worksheet
Dim lr As Long
Dim i As Long
Set sht1 = Worksheets("Sheet1")
lr = Fcst.Cells(Rows.Count, "A").End(xlUp).Row
With sht1
For i = 4 To lr
.Range("P" & i).Value = Application.Sum(Range("D" & i, "O" & i))
Next
End With
End Sub
Overall, this code works but it is very slow and I need to apply it to thousands of rows. I know that in order to make this faster, I need to turn the sum range into an array but I am not entirely sure how to do this when a loop is included.
Any help would be greatly appreciated.
Thanks,
G
Disclaimer: I know there are more efficient ways to sum cells together but this is just me playing around and learning.
Just do them all at once. Looping only adds time to process individual iterations.
With sht1.Range(sht1.cells(4, "P"), sht1.cells(lr, "P"))
.formula = "=sum(D4:O4)"
.Value = .value
End With
Use a variant array to limit the number of times that the vba accesses the worksheets:
Sub Loop_Test()
Dim sht1 As Worksheet
Set sht1 = Worksheets("Sheet1")
Dim fcst As Worksheet
Set fcst = Worksheets("Sheet2")
Dim lr As Long
lr = fcst.Cells(Rows.Count, "A").End(xlUp).Row
Dim dta As Variant
dta = fcst.Range(fcst.Cells(4, "D"), fcst.Cells(lr, "O")).Value
Dim otpt As Variant
ReDim otpt(1 To UBound(dta, 1), 1 To 1)
With sht1
Dim i As Long
For i = LBound(dta, 1) To UBound(dta, 1)
otpt(i, 1) = Application.Sum(Application.Index(dta, i, 0))
Next i
.Range("P4").Resize(UBound(dta, 1), 1).Value = otpt
End With
End Sub
Edit
The SUM(INDEX()) is slow it is quicker just to add the parts individually.
Sub Loop_Test()
Dim sht1 As Worksheet
Set sht1 = Worksheets("Sheet1")
Dim fcst As Worksheet
Set fcst = Worksheets("Sheet2")
Dim lr As Long
lr = fcst.Cells(Rows.Count, "A").End(xlUp).Row
Dim dta As Variant
dta = fcst.Range(fcst.Cells(4, "D"), fcst.Cells(lr, "O")).Value
Dim otpt As Variant
ReDim otpt(1 To UBound(dta, 1), 1 To 1)
With sht1
Dim i As Long
For i = LBound(dta, 1) To UBound(dta, 1)
Dim j as Long
For j = lbound(dta,2) to ubound(dta,2)
otpt(i, 1) = otpt(i, 1) + dta(i, j)
Next j
Next i
.Range("P4").Resize(UBound(dta, 1), 1).Value = otpt
End With
End Sub
Tested on 50,000 rows and result was near instantaneous.
Rather than looping over each row you can insert a summation formula into each row of column P with a single line of code:
.Range("P4:P" & lr).Formula="=SUM(D4:O4)"
assuming 4 is the starting row, and your variable lr is the last row.
Faster With an Array
Sub Loop_Test()
Const cSheet1 As Variant = "Sheet1"
Const cSheet2 As Variant = "Sheet2"
Const fr As Integer = 4
Dim sht1 As Worksheet
Dim fcst As Worksheet
Dim lr As Long
Dim i As Long
Dim vnt As Variant
Set sht1 = Worksheets(cSheet1)
Set fcst = Worksheets(cSheet2)
With fcst
lr = .Cells(.Rows.Count, "A").End(xlUp).Row
ReDim vnt(1 To lr - fr + 1, 1 To 1)
For i = 1 To UBound(vnt)
vnt(i, 1) = WorksheetFunction.Sum( _
.Range("D" & i + fr - 1, "O" & i + fr - 1))
Next
End With
sht1.Cells(fr, "P").Resize(UBound(vnt)) = vnt
End Sub

Copy from all rows one Worksheet and paste in to Another in alternate rows(Excel vba)

I have to copy rows from Worksheet A to Worksheet B. However, I have to paste in alternate rows like:
1 st row(Sheet A)-- 1 st row (Sheet B)
2nd row (Sheet A) --> 3rd row (Sheet B)
3rd row (Sheet A)--> 5th row(Sheet B)
Is this possible?
Thanks for your help.
You may try something like this...
Sub CopyRows()
Dim sws As Worksheet, dws As Worksheet
Dim i As Long, lr As Long
Application.ScreenUpdating = False
Set sws = Sheets("Sheet1")
Set dws = Sheets("Sheet2")
lr = sws.UsedRange.Rows.Count
dws.Cells.Clear
sws.UsedRange.Copy dws.Range("A1")
For i = lr To 1 Step -1
dws.Rows(i).Insert
Next i
Application.ScreenUpdating = True
End Sub
Edited Answer:
You may try this approach which will be faster enough to copy the data from Sheet1 into Sheet2. The only downside is it will copy the values only not the formulas if any on Sheet1.
Sub CopyRows()
Dim sws As Worksheet, dws As Worksheet
Dim i As Long, ii As Long, j As Long
Dim x, y()
Application.ScreenUpdating = False
Set sws = Sheets("Sheet1")
Set dws = Sheets("Sheet2")
dws.Cells.Clear
x = sws.Range("A1").CurrentRegion.Value
ReDim y(1 To UBound(x, 1) * 2, 1 To UBound(x, 2))
For i = 1 To UBound(x, 1)
j = j + 2
For ii = 1 To UBound(x, 2)
y(j, ii) = x(i, ii)
Next ii
Next i
dws.Range("A1").Resize(UBound(y, 1), UBound(y, 2)).Value = y
End Sub

Resources