Search column headers and insert new column if header does not already exist using Excel VBA - excel

I have a spreadsheet that is updated regularly. The user will update two columns on sheet(create) with container type (this is the header name) and the quantity, which will be transferred to sheet(Tracking). I am trying to figure out how to search sheet2(Tracking for existing headers (container types), if found then quantity will be updated within that column for the next available row. If header is not found, therefore a new column is added to the right with that new header name, as well as updating the quantity.
I did find some good example such as the below. However not sure how to apply it. Maybe there could be a way to loop it to search the headers.
Sub TrackR()
Dim cl As Range
For Each cl In Range("1:1")
If cl = sheets(“Create”).range(“J11:J36”) Then
cl.EntireColumn.Insert Shift:=xlToRight
End If
cl.Offset(0, 1) = "New Conatainer Name"
Next cl
Application.ScreenUpdating = False
Sheets("Tracking").Range("A" & Rows.Count).End(xlUp).Offset(1, 0) = Date
'Trailer No.
Sheets("Create").Range("L8").Copy
Sheets("Tracking").Range("B" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
'total container qty
Sheets("Create").Range("G43").Copy
Sheets("Tracking").Range("D" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
'Supplier
Sheets("Create").Range("K4").Copy
Sheets("Tracking").Range("C" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
'quantities
Sheets("Create").Range("L11").Copy
Sheets("Tracking").Range("E" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
Sheets("Create").Range("L12").Copy
Sheets("Tracking").Range("F" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
Sheets("Create").Range("L13").Copy
Sheets("Tracking").Range("G" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
Sheets("Create").Range("L14").Copy
Sheets("Tracking").Range("H" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
Sheets("Create").Range("L15").Copy
Sheets("Tracking").Range("I" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = False
End Sub

Not sure, try this ... ~
Sub TrackB()
Dim wsCreat As Worksheet: Set wsCreat = Sheets("Create")
Dim wsTracking As Worksheet: Set wsTracking = Sheets("Tracking")
Dim cl As Range, lastHCell As Range, header As Range, i As Integer, j As Integer,k as integer, str As Variant
With wsTracking
Set header = .[a1:xx1]: Set lastHCell = header.End(xlToRight)
iLstRow = .[a10000].End(xlUp).Offset(1, 0).Row
'Update default data [A:D]
.Range("A" & iLstRow) = Date
For Each str In Array("L8", "C4", "G43")
.Cells(iLstRow, i + 2) = wsCreat.Range(str): i = i + 1
Next
'add Column if not Match
For Each cl In wsCreat.[B11:B37, E11:E37]
Dim k: k = Application.Match(cl, header, 0)
If IsError(k) And cl <> vbNullString Then _
lastHCell.Offset(0, 1).EntireColumn.Insert Shift:=xlToRight, CopyOrigin:=True: _
Set lastHCell = lastHCell.Offset(0, 1): lastHCell.Value2 = cl
Next cl
'Update input Data
i = 5
Dim arr As Variant: arr = Array("B11:B37", "E11:E37")
Dim arrResult As Variant: arrResult = Array("C10" , "F10")
Dim cell As Range: k = 0
For k = 0 To UBound(arr)
j=1
For Each cell In wsCreat.Range(arr(k)).Cells
If cell.Value2 <> vbNullString Then
.Cells(iLstRow, Application.Match(cell, header, 0)) = wsCreat.Range(arrResult(k)).Offset(j, 0)
End If
j = j + 1
Next cell
Next
End With
End Sub

Untested but something like this should work:
Sub TrackR()
Dim wsTrack As Worksheet, wsCreate As Worksheet, cont, qty, h As Range
Dim c As Range, m, rw As Range, rngHeaders As Range, col As Long
Set wsCreate = ThisWorkbook.Worksheets("Create")
Set wsTrack = ThisWorkbook.Worksheets("Track")
'get the next empty row on the Tracking sheet
Set rw = wsTrack.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow
'fill in the common cells in the row
rw.Cells(1).Value = Date
rw.Cells(2).Value = wsCreate.Range("L8").Value
rw.Cells(3).Value = wsCreate.Range("K4").Value
rw.Cells(4).Value = wsCreate.Range("G43").Value
'now loop over the containers and add each one
Set rngHeaders = wsTrack.Cells(1, "E").Resize(1, 5000) 'or whatever would cover your data
For Each c In wsCreate.Range("J11:J36").Cells
cont = c.Value
qty = c.Offset(0, 2).Value
If Len(cont) > 0 And Len(qty) > 0 Then
m = Application.Match(cont, rngHeaders, 0) 'any existing match ?
If IsError(m) Then
'no match - find the first empty cell and add the container
Set h = rngHeaders.Cells(rngHeaders.Cells.Count).End(xlToLeft).Offset(0, 1)
h.Value = cont
col = h.Column 'column number for the added header
Else
'matched: get the column number
col = rngHeaders.Cells(m).Column
End If
rw.Cells(col).Value = qty '<< add the quantity
End If
Next c
End Sub

Related

What's the way to set the code so that it looks for the last filled row in column A, and then eliminate the rows where column H has values =0?

Good morning,
currently I have this code to delete rows without due date (Column J) and amount paid=0 (Column H).
Sub delete_rows()
Range("A1").End(xlDown).Select
Sheets("AA").Select
Range("J2").Select
ActiveCell.FormulaR1C1 = "=IF(RC[-5]=0,"""",RC[-5])"
Range("J2").Select
Selection.AutoFill Destination:=Range("J2:J500"), Type:=xlFillDefault
Range("J2").End(xlDown).Select
Range("K2").Select
Application.ScreenUpdating = False
With Sheet2
For line = .Cells(.Rows.Count, "J").End(xlUp).Row To 2 Step -1
If .Cells(line, "J") = "" Then
.Rows(line).Delete
End If
Next linha
End With
Application.ScreenUpdating = True
ActiveCell.FormulaR1C1 = "=IF(RC[-4]="""","""",RC[-4])"
Range("K2").Select
Selection.AutoFill Destination:=Range("K2:K500"), Type:=xlFillDefault
Range("K2").End(xlDown).Select
Range("J1").Select
Application.ScreenUpdating = False
With Sheet2
For line = .Cells(.Rows.Count, "K").End(xlUp).Row To 2 Step -1
If .Cells(line, "K") = "" Then
.Rows(line).Delete
End If
Next line
End With
Application.ScreenUpdating = True
End sub()
I created a code with a defined number of lines...however it takes a long time for the code to run, because sometimes the number of lines is small and it always runs the 500 lines. What's the way to set the code so that it looks for the last filled row in column A, and then eliminate the rows where column H has values =0 and in column J no values?
Please check: find last cell. Also have a look at: avoid select.
Afterwards, I think you should be able to understand the following code, which should get you the required result:
Sub test()
Application.ScreenUpdating = False
'declare your variables
Dim ws As Worksheet
Dim Rng1 As Range, Rng2 As Range
Dim i As Long, lastRow As Long
Set ws = Sheets("AA")
With ws
'get last row in
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
'set ranges for loop
Set Rng1 = Range(.Cells(2, "H"), .Cells(lastRow, "H"))
Set Rng2 = Range(.Cells(2, "J"), .Cells(lastRow, "J"))
'reverse loop
For i = Rng1.Rows.Count To 1 Step -1
'check conditions for cell in "H" and "J"
If Rng1.Cells(i) = 0 And Rng2.Cells(i) = "" Then
'defined ranges start at row 2, hence +1
ws.Rows(i + 1).Delete
End If
Next i
End With
Application.ScreenUpdating = True
End Sub

Loop the code till the cell is empty in excel

I stuck with this problem:
I have this code and it works but I struggle now.
I want the loop this whole code till in Table1 the cell D1 is empty.
Sub strule()
Dim myCellRange As Range
Worksheets("Table1").Select
Code = Range("D1")
Wert = Range("E10")
Worksheets("Table2").Select
Worksheets("Table2").Range("A1").Select
lMaxRows = Cells(Rows.Count, "A").End(xlUp).Row
Range("A" & lMaxRows).Select
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = Code
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Wert
Sheets("Table1").Select '
Rows("1:10").Select
Selection.Cut
Application.CutCopyMode = False
Selection.Delete Shift:=xlUp
End Sub
I've had a guess at what you want... could be completely wrong though.
First of all your original code with all the selecting & activating removed:
Sub strule()
Dim WrkSht1 As Worksheet
Set WrkSht1 = Worksheets("Table1")
'Worksheets("Table1").Select
Dim Code As String
Code = WrkSht1.Range("D1")
Dim Wert As String
Wert = WrkSht1.Range("E10")
Dim WrkSht2 As Worksheet
Set WrkSht2 = Worksheets("Table2")
'Worksheets("Table2").Select
'Worksheets("Table2").Range("A1").Select
Dim lMaxRows As Long
lMaxRows = WrkSht2.Cells(Rows.Count, "A").End(xlUp).Row
WrkSht2.Cells(lMaxRows + 1, 1) = Code 'Lastrow+1 in column A.
WrkSht2.Cells(lMaxRows + 1, 2) = Wert 'Lastrow+1 in column B.
'Range("A" & lMaxRows).Select
'ActiveCell.Offset(1, 0).Select
'ActiveCell.Value = Code
'ActiveCell.Offset(0, 1).Select
'ActiveCell.Value = Wert
WrkSht1.Rows("1:10").Delete shift:=xlUp
'Sheets("Table1").Select '
'Rows("1:10").Select
'Selection.Cut
'Application.CutCopyMode = False
'Selection.Delete Shift:=xlUp
End Sub
Now what I think you want:
Sub strule1()
Dim WrkSht1 As Worksheet
Set WrkSht1 = Worksheets("Table1")
Dim WrkSht2 As Worksheet
Set WrkSht2 = Worksheets("Table2")
Dim lLastRow1 As Long
lLastRow1 = WrkSht1.Cells(Rows.Count, "A").End(xlUp).Row
Dim x As Long
Dim lLastRow2 As Long
Dim Code As String
Dim Wert As String
For x = 1 To lLastRow1 Step 10
Code = WrkSht1.Cells(x, 4) 'Loop 1 grabs from row 1, loop 2 from row 11
Wert = WrkSht1.Cells(x + 9, 5) 'Loop 1 grabs from row 10, loop 2 from row 20
lLastRow2 = WrkSht2.Cells(Rows.Count, "A").End(xlUp).Row
WrkSht2.Cells(lLastRow2 + 1, 1) = Code 'Lastrow+1 in column A.
WrkSht2.Cells(lLastRow2 + 1, 2) = Wert 'Lastrow+1 in column B.
Next x
WrkSht1.Rows("1:" & x).Delete shift:=xlUp
End Sub

VBA: Looping a condition through a range that compares values from other columns until the list ends

Public Sub MainTOfomat()
Dim ShippingQty As Range
Dim ReceivedQty As Range
ActiveSheet.Columns("A:P").AutoFit
ActiveSheet.Range("A:P").AutoFilter Field:=13, Criteria1:="No"
ActiveSheet.Range("K:L").AutoFilter Field:=2, Criteria1:="<>"
Set ShippingQty = Range("K2")
Set ReceivedQty = ShippingQty.Offset(0, 1)
ShippingQty.Select
Do Until IsEmpty(ActiveCell)
If ShippingQty.Value = 0 Then
ShippingQty.Offset(0, 5) = "Needs Fulfillment"
ElseIf ShippingQty.Value > ReceivedQty.Value Then
ShippingQty.Offset(0, 5) = "Needs Receipt"
End If
ActiveCell.Offset(1, 0).Select
Loop
End Sub
The code is program is supposed to loop though each row in the column and fill in the statement based on the result of the condition for values in two other columns. The problem is that the loop goes through, but only the first line actually changes, and the auto filter code before the loop gets skipped.
Here is your macro fixed up.
As mentioned before your ShippingQty range and ReceivedQty do not change with the activecell. When moving to the next cell, that is the activecell. The filter range need to be the same. A:P is filtered, when changing to K:L ,field 2 actually becomes column B, so if you want to filter out non-blanks in column L you need the field 12.
Sub YourMacro()
Dim ShippingQty As Range
Dim ReceivedQty As Range
ActiveSheet.Columns("A:P").AutoFit
With ActiveSheet.Range("A:P")
.AutoFilter Field:=13, Criteria1:="No"
.AutoFilter Field:=12, Criteria1:="<>"
End With
Set ShippingQty = Range("K2")
Set ReceivedQty = ShippingQty.Offset(0, 1)
ShippingQty.Select
Do Until IsEmpty(ActiveCell)
If ActiveCell.Rows.Hidden = False Then
If ActiveCell.Value = 0 Then
ActiveCell.Offset(0, 5) = "Needs Fulfillment"
ElseIf ActiveCell.Value > ActiveCell.Offset(, 1).Value Then
ActiveCell.Offset(0, 5) = "Needs Receipt"
End If
End If
ActiveCell.Offset(1, 0).Select
Loop
ActiveSheet.AutoFilterMode = 0
End Sub
You can use this option as well without using selects.
Sub Option1()
Dim rng As Range, c As Range
Dim ws As Worksheet
Set ws = ActiveSheet
Application.ScreenUpdating = 0
With ws
Set rng = .Range("K2:K" & .Cells(.Rows.Count, "K").End(xlUp).Row)
With .Range("A:P")
.AutoFilter Field:=13, Criteria1:="No"
.AutoFilter Field:=12, Criteria1:="<>"
End With
For Each c In rng.SpecialCells(xlCellTypeVisible)
If c = 0 Then c.Offset(, 5) = "Needs Fulfillments"
If c > c.Offset(, 1) Then c.Offset(, 5) = "Needs Receipts"
Next c
.AutoFilterMode = False
End With
End Sub

how to fix Object variable or with block variable not set

Am newbie to VBA and trying to find dupliactes on column A and copy Column A, G and I to another sheet and used below code
Dim wstSource As Worksheet, _
wstOutput As Worksheet
Dim rngMyData As Range, _
helperRng As Range, _
unionRng As Range
Dim i As Long, iOld As Long
Set wstSource = Worksheets("Final Product List")
Set wstOutput = Worksheets("INN Working")
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With wstSource
Set rngMyData = .Range("A2:A" & .Range("A" & .Rows.Count).End(xlUp).Row)
End With
With rngMyData
Set helperRng = .Offset(, rngMyData.Columns.Count - 1).Resize(, 1)
Set unionRng = .Cells(1000, 1000) 'set a "helper" cell to be used with Union method, to prevent it from failing the first time
End With
With helperRng
.FormulaR1C1 = "=row()" 'mark rows with ad ascending number (its own row number)
.Value = .Value
End With
With rngMyData.Resize(, rngMyData.Columns.Count + 1) 'enclose "helper" column
.Sort key1:=.Columns(1), Order1:=xlAscending, Orientation:=xlTopToBottom, Header:=xlNo ' sort data to have all same columnA values grouped one after another
i = .Rows(1).Row 'start loop from data first row
Do While i < .Rows(.Rows.Count).Row
iOld = i 'set current row as starting row
Do While .Cells(iOld + 1, 1) = .Cells(iOld, 1) 'loop till first cell with different value
iOld = iOld + 1
Loop
If iOld - i > 0 Then Set unionRng = Union(unionRng, .Cells(i, 1).Resize(iOld - i + 1)) 'if more than one cell found with "current" value, then add them to "UnionRng" range
i = iOld + 1
Loop
Intersect(unionRng, rngMyData).EntireRow.Copy Destination:=wstOutput.Cells(1, 1) 'get rid of the "helper" cell via Intersect method
wstOutput.Columns(helperRng.Column).Clear 'delete "Helper" column pasted in wstOutput sheet
.Sort key1:=.Columns(4), Order1:=xlAscending, Orientation:=xlTopToBottom, Header:=xlNo ' sort data in wstSource back
End With
helperRng.Clear 'delete "helper" column, not needed anymore
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
The above code is throwing RunTime error '91'
Object variable or with block variable not set
If iOld - i > 0 Then Set unionRng = Union(unionRng, .Cells(i, 1).Resize(iOld - i + 1)) 'if more than one cell found with "current" value, then add them to "UnionRng" range
i = iOld + 1
End If
The End If was missing and VBA gives a poor message.

How to create a textjoin worksheet function with dynamic range

I have data where I have many column headers. One of the header is "Text" and one other header is "Value Date". I want to combine the values contained in every row between these columns in another column row-wise.
The problem is the number of columns between these two headers is not constant. It changes with every new ledger I export. So I want my code to be dynamic in such a way that it will identify the column of "Text" and then it will identify the column of "Value Date" and combine everything between in another column row-wise.
This is where I have reached with my code but I don't know why it's not working. I have been trying this for last 3 days only to get nowhere. When I run this code, the result which I get is "TextColumnNo:ValueColumnNo".
Sub TextJoin()
Dim TextColumnNo As Range
Dim ValueColumnNo As Range
Range("A1").Select
ActiveCell.EntireRow.Find("Text").Activate
Set TextColumnNo = Range(ActiveCell.Address(False, False))
Range("A1").Select
ActiveCell.EntireRow.Find("Value").Activate
Set ValueColumnNo = Range(ActiveCell.Address(False, False))
ActiveCell.Offset(1, -1).Select
Application.CutCopyMode = False
ActiveCell.Value = Application.WorksheetFunction.TextJoin(" ", True, _
"TextColumnNo:ValueColumnNo")
ActiveCell.Select
Selection.AutoFill Destination:=ActiveCell.Range("A1:A8524")
ActiveCell.Range("A1:A8524").Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End Sub
You would need 2 loops for this. One looping through all rows and one looping through the columns to combine the text for each row.
Note that you need to adjust some things like sheet name and output column here.
Option Explicit
Public Sub TextJoin()
Dim ws As Worksheet
Set ws = Worksheets("Sheet1") 'define a worksheet
'find start
Dim FindStart As Range
Set FindStart = ws.Rows(1).Find("Text")
If FindStart Is Nothing Then
MsgBox "start not found"
Exit Sub
End If
'find end
Dim FindEnd As Range
Set FindEnd = ws.Rows(1).Find("Value Date")
If FindEnd Is Nothing Then
MsgBox "start not found"
Exit Sub
End If
'find last used row in column A
Dim lRow As Long
lRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim iRow As Long
For iRow = 2 To lRow 'loop through all rows (2 to last used row)
Dim CombinedText As String
CombinedText = vbNullString 'initialize/reset variable
Dim iCol As Long 'loop through columns for each row (from start to end column)
For iCol = FindStart.Column To FindEnd.Column
CombinedText = CombinedText & ":" & ws.Cells(iRow, iCol).Text 'combine values
Next iCol
ws.Range("Z" & iRow) = CombinedText 'write values in column Z
Next iRow
End Sub
Sub TextJoin()
Dim ColRefText As Long
Dim ColRefValueDate As Long
Const firstcol = "Text"
Const secondcol = "Value Date"
Dim r As Range
Set r = Rows(1).Cells.Find(firstcol)
If Not r Is Nothing Then
ColRefText = r.Column
Set r = Rows(1).Cells.Find(secondcol)
If Not r Is Nothing Then
ColRefValueDate = r.Column
End If
End If
If ColRefValueDate + ColRefText > 0 Then
With Cells(2, Worksheets(1).Columns.Count).End(xlToLeft).Offset(0, 1)
.Formula = Replace("=" & Cells(2, ColRefText).AddressLocal & "&" & Cells(2, ColRefValueDate).AddressLocal, "$", "")
.Copy Range(.Address, Cells(ActiveSheet.UsedRange.Rows.Count, .Column).Address)
End With
End If
End Sub

Resources