Out of a file with approximately 50.000 rows I want to delete rows which don't have a specific number in column B. I use this code:
Sub DelRows()
Application.ScreenUpdating = False
Worksheets("2016").Activate
lastrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = lastrow To 2 Step -1
If Cells(i, "B").Value <> "1060" And _
Cells(i, "B").Value <> "1061" And _
Cells(i, "B").Value <> "1062" And _
Cells(i, "B").Value <> "1063" And _
Cells(i, "B").Value <> "1064" And _
Cells(i, "B").Value <> "1105" And _
Cells(i, "B").Value <> "11050" And _
Cells(i, "B").Value <> "11051" And _
Cells(i, "B").Value <> "11053" And _
Cells(i, "B").Value <> "11054" And _
Cells(i, "B").Value <> "1160" And _
Cells(i, "B").Value <> "1161" And _
Cells(i, "B").Value <> "1162" And _
Cells(i, "B").Value <> "1163" And _
Cells(i, "B").Value <> "1164" And _
Cells(i, "B").Value <> "1166" And _
Cells(i, "B").Value <> "1168" And _
Cells(i, "B").Value <> "1169" And _
Cells(i, "B").Value <> "8060" And _
Cells(i, "B").Value <> "8061" And _
Cells(i, "B").Value <> "8062" And _
Cells(i, "B").Value <> "8063" And _
Cells(i, "B").Value <> "8064" And _
Cells(i, "B").Value <> "8068" And _
Cells(i, "B").Value <> "8192" Then
Cells(i, "B").EntireRow.Delete
End If
Next i
End Sub
This macro takes a lot of time and it seems to be that there is a maximum of 'and-statements'.
I tried to figure it out with an array or a filter, but it's hard for me as a beginner.
I would like to put the numbers on a separate worksheet as a range e.g.:
A
1 1060
2 1061
3 1062
4 1063
5 1064
…
I've tried to figure it out with section Criteria range on a different sheet* on https://www.rondebruin.nl/win/winfiles/MoreDeleteCode.txt, but I don't fully understand this VBA code.
Can somebody please help me?
Kind regards,
Richard
Let's say the values are as in the code below - rngCheck and rngDelete.
A nested loop can do exactly this job. The outer loop goes through the range, which should be deleted rngDelete and the inner goes through the checking values rngCheck.
If a matching value is found, it is deleted and the inner loop is exited. As far as we are looping through rows and we need to delete some of them, the for loop is with reversed counting:
Option Explicit
Public Sub TestMe()
Dim cnt As Long
Dim rngDelete As Range
Dim rngCheck As Range
Dim rngCell As Range
Set rngCheck = Worksheets(2).Range("A1:A2")
Set rngDelete = Worksheets(1).Range("A1:A20")
For cnt = rngDelete.Rows.Count To 1 Step -1
For Each rngCell In rngCheck
If rngCell = rngDelete.Cells(cnt, 1) Then
rngDelete.Rows(cnt).Delete
Exit For
End If
Next rngCell
Next cnt
End Sub
Here's an array approach which saves on reading from and writing to spreadsheets and so should be a bit quicker. This method includes the cells which do match rather than excluding those which don't. Adjust your range of cells against which you are checking accordingly. I have assumed your data start in A1 of sheet 2016.
Sub DelRows()
Dim v, i As Long, j As Long, vOut(), k As Long, rExcl As Range
Set rExcl = Sheets("Sheet2").Range("A1:A5") 'adjust accordingly
With Worksheets("2016")
v = .Range("A1").CurrentRegion.Value
.Range("A1").CurrentRegion.Offset(1).ClearContents
ReDim vOut(1 To UBound(v, 1), 1 To UBound(v, 2))
For i = LBound(v, 1) To UBound(v, 1)
If IsNumeric(Application.Match(v(i, 2), rExcl, 0)) Then
j = j + 1
For k = LBound(v, 2) To UBound(v, 2)
vOut(j, k) = v(i, k)
Next k
End If
Next i
.Range("A2").Resize(j, UBound(v, 2)) = vOut
End With
End Sub
Related
What I'm trying to do is remove any rows where a cell value in a specific column matches what is defined to remove. After that is done re-sequence the value in another column by group.
Using the example below:
I want to look at column B and remove any rows that have a value of A or C. Then I want to basically renumber after the dot (.) in column A to reset itself.
Before Macro Code Fig. 1
After value A and C are removed Fig. 2
Final list after column A is renumbered Fig. 3
I figured out how to remove the rows using this code, but stuck on what to do next:
Sub DeleteRowBasedOnCriteriga()
Dim RowToTest As Long
For RowToTest = Cells(Rows.Count, 2).End(xlUp).Row To 2 Step -1
With Cells(RowToTest, 2)
If .Value = "A" Or .Value = "C" _
Then _
Rows(RowToTest).EntireRow.Delete
End With
Next RowToTest
End Sub
This will be easier to do looping from the top down (using step 1 instead of step -1). I've tried to stay true to your original coding and made this:
Sub DeleteRowBasedOnCriteriga()
Dim RowToTest As Long
Dim startRow As Long
Dim i As Integer
startRow = 2
'Clear the rows that have "A" or "C" in column B
For RowToTest = Cells(Rows.Count, 1).End(xlUp).Row to startRow To Step -1
With Cells(RowToTest, 2)
If .Value = "A" Or .Value = "C" _
Then _
Rows(RowToTest).EntireRow.Delete
End With
Next RowToTest
'If the left 3 characters of the cell above it are the same,_
'then increment the renumbering scheme
For RowToTest = startRow To Cells(Rows.Count, 1).End(xlUp).Row
If Left(Cells(RowToTest, 1).Value, InStr(1, Cells(RowToTest, 1), "\")) = Left(Cells(RowToTest, 1).Offset(-1, 0).Value, InStr(1, Cells(RowToTest, 1), "\")) Then
i = i + 1
Cells(RowToTest, 1).Value = Left(Cells(RowToTest, 1).Value, InStr(1, Cells(RowToTest, 1), ".")) & i
Else
i = 0
Cells(RowToTest, 1).Value = Left(Cells(RowToTest, 1).Value, InStr(1, Cells(RowToTest, 1), ".")) & i
End If
Next RowToTest
End Sub
EDIT: I've updated it to compare all of the string before the backslash and compare using that.
EDIT++: It has been brought to my attention that when deleting rows it is better to work from the bottom up (step -1) to ensure every row is accounted for. I've re-implemented the original steps in the first code.
Admittedly, this isn't probably the most efficient, but it should work.
Sub DeleteRowBasedOnCriteriga()
Dim RowToTest As Long, i As Long
Application.ScreenUpdating = False
For RowToTest = Cells(Rows.Count, 2).End(xlUp).Row To 2 Step -1
With Cells(RowToTest, 2)
If .Value = "A" Or .Value = "C" Then Rows(RowToTest).EntireRow.Delete
End With
Next RowToTest
Dim totalRows As Long
totalRows = Cells(Rows.Count, 1).End(xlUp).Row
Dim curCelTxt As String, aboveCelTxt As String
For i = totalRows To i Step -1
If i = 1 Then Exit For
curCelTxt = Left(Cells(i, 1), WorksheetFunction.Search("\", Cells(i, 1)))
aboveCelTxt = Left(Cells(i - 1, 1), WorksheetFunction.Search("\", Cells(i - 1, 1)))
If curCelTxt = aboveCelTxt Then
Cells(i, 1).Value = ""
Else
Cells(i, 1).Value = WorksheetFunction.Substitute(Cells(i, 1), Right(Cells(i, 1), Len(Cells(i, 1)) - WorksheetFunction.Search(".", Cells(i, 1))), "0")
End If
Next i
Dim rng As Range, cel As Range
Dim tempLastRow As Long
Set rng = Range("A1:A" & Cells(Rows.Count, 2).End(xlUp).Row)
For Each cel In rng
If cel.Offset(1, 0).Value = "" Then
tempLastRow = cel.End(xlDown).Offset(-1, 0).Row
If tempLastRow = Rows.Count - 1 Then
tempLastRow = Cells(Rows.Count, 2).End(xlUp).Row
cel.AutoFill Destination:=Range(cel, Cells(tempLastRow, 1))
Exit For
Else
cel.AutoFill Destination:=Range(cel, Cells(tempLastRow, 1))
End If
End If
Next cel
Application.ScreenUpdating = True
End Sub
Mainly, I discovered that you can use AutoFill to fix the last number in the string. Meaning if you AutoFill this text, CAT\Definitions.0 down, you get the number updating as you drag/fill.
The following script selects a range of data on one sheet and transfers the selection to another sheet.
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
For i = 6 To LastRow
If Cells(i, 1) <> "" And Cells(i, 21) = "OK" And Cells(i, 22) <> "Yes" Then
Range(Cells(i, 1), Cells(i, 4)).Select
Selection.Copy
erow = Worksheets("iForms").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
Worksheets("iForms").Cells(erow, 1).PasteSpecial Paste:=xlPasteValues
If Cells(i, 1) <> "" Then Cells(i, 22).Value = "Yes"
If Cells(i, 22) <> "" Then Cells(i, 23).Value = Now
If Cells(i, 23) <> "" Then Cells(i, 24).Value = Environ("UserName")
ActiveWorkbook.Save
End If
Next i
I would now like to introduce a script which will replace the row of data on the target sheet if the value in column A already exists, but i'm not sure how to achieve this, any help is much appreciated.
Thank you in advance.
Public Function IsIn(li, Val) As Boolean
IsIn = False
Dim c
For Each c In li
If c = Val Then
IsIn = True
Exit Function
End If
Next c
End Function
dim a: a= range(destWB.sheet(whatever)..range("A1"),destWB.Range("A" & destWB.sheet(whatever).Rows.Count).End(xlUp)).value
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
For i = 6 To LastRow
if isin(a, Cells(i, 1) ) then
do whatever you want
else
If Cells(i, 1) <> "" And Cells(i, 21) = "OK" And Cells(i, 22) <> "Yes" Then
Range(Cells(i, 1), Cells(i, 4)).Select
Selection.Copy
erow = Worksheets("iForms").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
Worksheets("iForms").Cells(erow, 1).PasteSpecial Paste:=xlPasteValues
If Cells(i, 1) <> "" Then Cells(i, 22).Value = "Yes"
If Cells(i, 22) <> "" Then Cells(i, 23).Value = Now
If Cells(i, 23) <> "" Then Cells(i, 24).Value = Environ("UserName")
ActiveWorkbook.save
End If
End If
Next i
I suggest using a Dictionary-Object which is most likely a Hash-Map. The advantage is that you can use the built in method Dictionary.Exists(Key) to check if the Dictionary already holds the specified value (Key).
Also you should not save the Workbook in every step of the iteration. It would be better (and faster) to only save the workbook after completing the copying of your whole data.
Additionally your If-Tests after copy-paste are not neccessary, because you are already checking for Cells(i,1)<>"" before copying so you don't have to check this again as it does not change.
The following code shows how to get your desired result:
Set dict = CreateObject("Scripting.Dictionary")
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
For i = 6 To LastRow
If Cells(i, 1) <> "" And Cells(i, 21) = "OK" And Cells(i, 22) <> "Yes" Then
If dict.Exists(Cells(i,1).Value) Then
'value already exists -> update row number
dict.Item(Cells(i,1).Value)=i
Else
'save value of column A and row number in dictionary
dict.Add Cells(i,1).Value, i
End If
Cells(i, 22).Value = "Yes"
Cells(i, 23).Value = Now
Cells(i, 24).Value = Environ("UserName")
End If
Next i
'finally copy over your data (only unique values)
For Each i In dict.Items
Range(Cells(i, 1), Cells(i, 4)).Select
Selection.Copy
erow = Worksheets("iForms").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
Worksheets("iForms").Cells(erow, 1).PasteSpecial Paste:=xlPasteValues
Next i
I have this piece of code: I would like to do a left function on cells G and M in the following code: I am having an issue as when I try:
If left(.Cells(i, "G",4)) <> left(.Cells(i, "M",4)) this does not work.
any advice?
Here is the full code:
Sub SingleTradeMove()
Dim wsTD As Worksheet
Set wsTD = Worksheets("Trade data")
Sheets("Sheet2").Range("A2:AK600").ClearContents
With wsTD
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 2 To lastRow
If .Cells(i, "G") <> .Cells(i, "M") _
Or .Cells(i, "I") <> .Cells(i, "O") _
Or .Cells(i, "L") <> .Cells(i, "R") Then
.Cells(i, "J").EntireRow.Copy _
Destination:=Sheets("Sheet2").Range("A" &
Rows.Count).End(xlUp).Offset(1)
End If
Next i
End With
End Sub
You need to use
If left(.Cells(i, "G"),4) <> left(.Cells(i, "M"),4)
I am new here.
I am trying to build a quick VBA program to "flatten" a Bill of Materials by heirarchy (BOM Level) and Status.
Here is some sample data:
The sample data shows a BOM with a Car as a top level assembly, Wheel and Engine as second level assemblies, and various children parts that make up those assemblies on the third and fourth level of the BOM.
I want to delete any rows that have the value "ZE", "ZM", or blank in column C.
I also want to delete any rows that have the value "ZA" and are also direct children of another "ZA" item. (Example - Delete the Rim row from the BOM because the Wheel is the Parent "ZA" item)
Here is what I have so far:
Sub deletechildren()
Dim lr As Long, i As Long, k As Long
lr = Cells(Rows.Count, 1).End(xlUp).Row
For i = lr To 1 Step -1
If i > 2 Then
k = i - 1
End If
If Cells(i, 3).Value = "ZA" And Cells(i, 1).Value = Cells(k, 1).Value Then
Cells(i, 3).EntireRow.Delete
ElseIf Cells(i, 3).Value = "ZE" Then
Cells(i, 3).EntireRow.Delete
ElseIf Cells(i, 3).Value = "ZM" Then
Cells(i, 3).EntireRow.Delete
ElseIf Cells(i, 3).Value = "" Then
Cells(i, 3).EntireRow.Delete
End If
Next i
lr = Cells(Rows.Count, 1).End(xlUp).Row
End Sub
I am getting some error on the first part of the If statement, where I want to parse out any "ZA" status children from the "ZA" parent.
Any ideas?
Sub DeleteChildren()
Dim lastRow As Long
Dim i As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lastRow
If (Cells(i, 3).Value = "ZE" Or Cells(i, 3).Value = "ZM" Or Cells(i, 3).Value = "") And Cells(i, 1) <> "" Then
Rows(i).EntireRow.Delete xlShiftUp
i = i - 1
GoTo NextIteration
End If
If Cells(i, 1).Value > 1 Then
If (Cells(i, 3).Value = "ZA" And Cells(i - 1, 3).Value = "ZA") And Not Cells(i, 1).Value < Cells(i - 1, 1).Value Then ' This way is a there are multiple levels with "ZA" there can
Cells(i, 5).Value = "Delete"
End If
End If
NextIteration:
Next i
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To lastRow
If Cells(i, 5).Value = "Delete" Then
Rows(i).EntireRow.Delete xlShiftUp
i = i - 1
End If
Next i
End Sub
A part of the problem is that the variable k is not being used to correctly identify parent/children relationships (if I understand your requirements correctly). In your case, you are comparing the each value with the row above it, but in a bill of materials, the parent row might be multiple rows above, and is denoted by a hierarchy value - 1.
See my revised code below. In the code, we first delete any rows that we know to delete (ZM, ZE, and Blanks). Next, we loop up the hierarchy values until we find one hierarchy value above the current row. That becomes the parent row, and from there, we test.
Let me know if you need additional help.
Sub deletechildren()
Dim lr As Long, i As Long, k As Long
lr = Cells(Rows.Count, 1).End(xlUp).Row
For i = lr To 1 Step -1
If i > 2 Then
k = i - 1
If Cells(i, 3) = "ZE" Or Cells(i, 3) = "ZM" Or Cells(i, 3) = "" Then
Rows(i).Delete
Else
k = i - 1
Do Until i <= 2 Or (Cells(i, 1) - Cells(k, 1) = 1)
k = k - 1
Loop
'Now, k represents the parent row.
If Cells(i, 3) = "ZA" And Cells(k, 3) = "ZA" Then
Rows(i).Delete
End If
End If
End If
Next i
lr = Cells(Rows.Count, 1).End(xlUp).Row
End Sub
I'd use Autofilter() and Sort() methods of Range object like follows:
Option Explicit
Sub deletechildren()
Dim i As Long
With Worksheets("BOM")
With .Range("A1:D" & .Cells(.Rows.Count, 1).End(xlUp).Row)
.AutoFilter Field:=3, Criteria1:=Array("ZE", "ZM", "="), Operator:=xlFilterValues
With .Offset(1).Resize(.Rows.Count - 1)
If Application.WorksheetFunction.Subtotal(103, .Columns(1)) > 1 Then .SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
.AutoFilter
.Sort key1:=Range("C1"), order1:=xlAscending, key2:=Range("A1"), order2:=xlAscending, Header:=xlYes
i = .Rows(.Rows.Count).Row
Do Until .Cells(i, 1) = .Cells(2, 1)
i = i - 1
Loop
If i < .Rows.Count Then .Rows(i + 1).Resize(.Rows.Count - i).EntireRow.Delete
End With
End With
End Sub
I’m new to VBA and have been searching the solution from long time.
I need the data to perform Vlookup with another set of data having common IDs
I have data like given below.
ID Status Package
0001 ACT Gold
0001 ACT SSA
0001 ACT SP
0002 ACT Silver
0003 DIS SSA
0003 DIS SSB
0004 ACT PT
0005 DIS NP
0006 DIS <Blank >
And I need it in following structure
ID Status Package1 Package 2 Package 3….
0001 ACT Gold SSA SP
0002 ACT Silver
0003 DIS SSA SSB
0004 ACT PT
0005 DIS NP
0006 DIS
Number of packages can vary from 0 to 15.
Also how to do reverse operation? (secondary requirement)
Tried this code (!) but result for values with 1 or no parameters was not accurate.
Sub test()
Dim Dic As Object: Set Dic = CreateObject("Scripting.Dictionary")
Dim CLa As Range, CLb As Range, x&, Names$, ID$, Key
' Sheet1 is a Source Sheet
' Sheet3 is a Target Sheet
x = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
For Each CLa In Sheets("Sheet1").Range("A1:A" & x)
If Not Dic.exists(CStr(CLa.Value)) Then
ID = CLa.Value
' Sheet1 is a Source Sheet
' Sheet3 is a Target Sheet
For Each CLb In Sheets("Sheet1").Range("A1:A" & x)
If CLb.Value = ID Then
If Names = "" Then
Names = CLb.Offset(, 2).Value
Else
Names = Names & "," & CLb.Offset(, 2).Value
End If
End If
Next CLb
Dic.Add ID, Names
End If
ID = Empty: Names = Empty
Next CLa
x = 1
For Each Key In Dic
Sheets("Sheet3").Cells(x, 1).Value = Key
Sheets("Sheet3").Range(Cells(x, 2), Cells(x, 4)) = Split(Dic(Key), ",")
x = x + 1
Next Key
Sheets("Sheet3").Cells.Replace "#N/A", Replacement:=""
End Sub
I would use a simple formula:
="Package"&COUNTIF($A$1:$A2;A2)
It creates your matching names and then you can use a Pivot to summarize. Of course you could set up loops through for each separate ID.
The reverse is more tricky, I'd resort to something along the lines of
Sub ertdfgcvb()
Dim ws As Worksheet, wsex As Worksheet, k As Long, i As Long, j As Long, LastRow As Long
Set ws = Sheets("tabular form")
Set ws = Sheets("dataset form")
k = 2
LastRow = ws.Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
For i = 2 To LastRow 'from the second to the last row
j = 3 'start at Package1
While Not IsEmpty(ws.Cells(i, j))
wsex.Cells(k, 1).Value2 = ws.Cells(i, 1).Value2 'copies the ID from the A column
wsex.Cells(k, 2).Value2 = ws.Cells(i, 2).Value2 'Copies status likewise
wsex.Cells(k, 3).Value2 = ws.Cells(i, j).Value2 'copies Package<n>
'wsex.Cells(k, 4).Value2 = "Package" & j - 2 'copies "Package<n>"
k = k + 1 'increases counters
j = j + 1
Wend
Next i
End Sub
This can be done with a simple loop
Sub Transpose()
writeRow = 1
LastRow = Columns(1).Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For i = 2 To LastRow
If Cells(i, 1).Value <> currentID Then
'New ID
writeRow = writeRow + 1
currentID = Cells(i, 1).Value
Cells(writeRow, 5).Value = currentID
Cells(writeRow, 6).Value = Cells(i, 2).Value
Cells(writeRow, 7).Value = Cells(i, 3).Value
Else
'Continue from old ID
Cells(writeRow, Rows(writeRow).Find("*", searchorder:=xlByColumns, searchdirection:=xlPrevious).Column + 1).Value = Cells(i, 3).Value
End If
Next i
End Sub
I didn't read that you also need a reverse routine. Try these:
Sub Transpose()
Cells(2, 5).CurrentRegion.ClearContents
writeRow = 1
For i = 2 To Columns(1).Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
If Cells(i, 1).Value <> currentID Then
'New ID
writeRow = writeRow + 1
currentID = Cells(i, 1).Value
Cells(writeRow, 5).Value = currentID
Cells(writeRow, 6).Value = Cells(i, 2).Value
Cells(writeRow, 7).Value = Cells(i, 3).Value
writeCol = 8
Else
'Continue from old ID
Cells(writeRow, writeCol).Value = Cells(i, 3).Value
writeCol = writeCol + 1
End If
Next i
End Sub
Sub ReverseTranspose()
Cells(2, 1).CurrentRegion.ClearContents
writeRow = 1
For i = 2 To Columns(5).Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For j = 7 To WorksheetFunction.Max(7, Rows(i).Find("*", searchorder:=xlByColumns, searchdirection:=xlPrevious).Column)
writeRow = writeRow + 1
currentID = Cells(i, 5).Value
Cells(writeRow, 1).Value = currentID
Cells(writeRow, 2).Value = Cells(i, 6).Value
Cells(writeRow, 3).Value = Cells(i, j).Value
Next j
Next i
End Sub