I am trying to write a VBA Excel Macro to look through hundreds of thousands of lines of data to make sure that each unique entry in column A has a number of entries equal to column C.
For example:
Source Account Id 84512 occurs 6 times but there needs to be 12 occurrences (as indicated by column C). This means I need to add 6 lines, before (or after) the existing 6 lines.
Next we see Source Account Id 64857 occurs once but needs to occur 5 times. I would add 4 lines above and have the same Source Account Id code and the same Account Name. The rest of the cells can be "0".
Here is an example of the finished product:
Here is what I have so far:
Sub InsertRowAtChangeInValue()
Dim lRow As Long
Dim nMonths As Long
For lRow = Cells(Cells.Rows.count, "A").End(xlUp).Row To 2 Step -1
nMonths = 12 - Cells(Application.ActiveCell.Row, 3).Value
If Cells(lRow, "A") <> Cells(lRow - 1, "A") Then Rows(lRow).EntireRow.Resize(nMonths).Insert
Next lRow
End Sub
Please let me know if you have any suggestions.
*All data in these examples is fictional
Try this after renaming the referenced worksheet.
Sub expandMonths()
'https://stackoverflow.com/questions/52304181
Dim i As Long, j As Long, m As Long, a As Variant
With Worksheets("sheet1")
i = .Cells(.Rows.Count, "A").End(xlUp).Row
Do While i > 1
a = Array(.Cells(i, "A").Value2, .Cells(i, "B").Value2, 0, 0, 0, 0)
m = .Cells(i, "C").Value2
j = Application.Match(.Cells(i, "A").Value2, .Columns("A"), 0)
If i - j < m Then
.Rows(j).Resize(m - (i - j) - 1, 1).EntireRow.Insert
.Cells(j, "A").Resize(m - (i - j) - 1, UBound(a) + 1) = a
.Cells(j, "C").Resize(m - (i - j) - 1, 4).NumberFormat = "0"
End If
i = j - 1
Loop
End With
End Sub
Related
I'm new to VBA so this is probably a very obvious mistake.
To keep it short, I am trying to delete rows based on two criteria: In Column A, if they have the same value (duplicate) and in Column B, the difference is less than 100, then one row is deleted from the bottom.
Example data:
Column A Column B
1 300
1 350 SHOULD be deleted as second column diff. is <100 compared to row above
2 500
2 700 Should NOT be deleted as second column diff. is not <100
Here is the code I have come up with:
Sub deduplication()
Dim i As Long
Dim j As Long
Dim lrow As Long
Application.ScreenUpdating = False
With Worksheets("Sheet1")
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = lrow To 2 Step -1
For j = i To 2 Step -1
If .Cells(i, "A").Value = .Cells(j, "A").Value And .Cells(i, "B").Value - .Cells(j, "B").Value < 100 Then
.Cells(i, "A").EntireRow.Delete
End If
Next j
Next i
End With
End Sub
This largely works, but only if the second criterion is greater than (>) rather than less than (<). When it is less than, it deletes every row. What am I doing wrong? Is there an easy fix?
Thank you
Not
If .Cells(i, "A").Value = .Cells(j, "A").Value And .Cells(i, "B").Value - .Cells(j, "B").Value < 100 Then
Here in the second part of the statement, you're just comparing .Cells(j, "B").Value to const 100 !
But
If .Cells(i, "A").Value = .Cells(j, "A").Value And Abs(.Cells(i, "B").Value - .Cells(j, "B").Value) < 100 Then
Abs() may help, else keep just the ( )
Something like this should work for you:
Sub tgr()
Dim ws As Worksheet
Dim rDel As Range
Dim rData As Range
Dim ACell As Range
Dim hUnq As Object
Set ws = ActiveWorkbook.Sheets("Sheet1")
Set hUnq = CreateObject("Scripting.Dictionary")
Set rData = ws.Range("A2", ws.Cells(ws.Rows.Count, "A").End(xlUp))
If rData.Row = 1 Then Exit Sub 'No data
For Each ACell In rData.Cells
If Not hUnq.Exists(ACell.Value) Then
'New Unique ACell value
hUnq.Add ACell.Value, ACell.Value
Else
'Duplicate ACell value
If Abs(ws.Cells(ACell.Row, "B").Value - ws.Cells(ACell.Row - 1, "B").Value) < 100 Then
If rDel Is Nothing Then Set rDel = ACell Else Set rDel = Union(rDel, ACell)
End If
End If
Next ACell
If Not rDel Is Nothing Then rDel.EntireRow.Delete
End Sub
Sticking to the format of your code, you can do this using one For loop as well.
For i = lrow To 3 Step -1
If .Cells(i, "A") = .Cells(i - 1, "A") And (.Cells(i, "B") - .Cells(i - 1, "B")) < 100 Then
.Cells(i, "A").EntireRow.Delete
End If
Next i
Every first j-cycle starts off by comparing a row to itself since you start with j = i. The difference between a value and itself is always zero. (It also compares row 2 with itself as the very last step.)
However, if you switch:
For i = lrow To 2 Step -1
For j = i To 2 Step -1
to:
For i = lrow To 3 Step -1
For j = i - 1 To 2 Step -1`
the code will compare all the various rows without the self-compares.
Another point (which #Proger_Cbsk 's answer brought to mind), is that doing the comparison with just the subtraction .Cells(i, "B").Value - .Cells(j, "B").Value < 100 will sometimes cause unexpected results.
For example, assume .Cells(i, "B").Value = 1 and .Cells(j, "B").Value = 250. We can tell by just looking, that there is a difference of at least 100, so you would expect this part of the expression to evaluate to False. However, from straight substitution, you get the expression: 1 - 250 < 100. Since 1 - 250 = -249, and since -249 < 100, the expression would actually evaluate to True.
However, if you were to change .Cells(i, "B").Value - .Cells(j, "B").Value < 100 to Abs(.Cells(i, "B").Value - .Cells(j, "B").Value) < 100, the expression will now be looking at if the difference is greater or less than 100, instead of looking at if the subtraction result is greater or less than 100.
Why not to use the built-in command:
Worksheets("Sheet1").Range("$A:$A").RemoveDuplicates Columns:=1, Header:=xlYes
Range.RemoveDuplicates Method (Excel)
I wnat to do some grouping in excel using VBA
My "criteria" column is "A" which is general a list of number that are obviously redundant and should be grouped to give the user a better understanding of the excel sheet
I have named column "A" "Vertrag__Nr."
My Code
Sub Test()
Dim i As Integer, LastRow As Integer
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To LastRow
If Not Left(Cells(i, 1), 2) = "Vertrag__Nr." Then
Cells(i, 2).EntireRow.Group
End If
Next i
End Sub
My problem is that my code instead of grouping by entries "Vertrag _Nr." (Column A) In groups the whole column into one big groupe
as grouping is used for summaries, there has to be a place for summary between groups, they cannot be contiguous, try this code:
Sub Test()
Dim i As Integer, j As Integer, LastRow As Integer
Dim currVal As Variant
With ActiveSheet
LastRow = .Cells(Rows.Count, 1).End(xlUp).Row
i = 2
While i <= LastRow
currVal = .Cells(i, 1).Value
j = 0
Do
j = j + 1
Loop Until .Cells(i + j, 1).Value <> currVal
If j > 1 Then
.Rows(i + j).Insert
.Cells(i + j, 1).Value = currVal
Range(.Cells(i, 1), .Cells(i + j - 1, 1)).EntireRow.Group
End If
i = i + j
Wend
End With
End Sub
In my Excel worksheet I have several values I need to compare and sum up in case defined criteria match.
The worksheet contains these information:
Name(A), Date(B), Hours worked(C), other information(D-H).
Via VBA I want to check if Hours worked exceeds the value "10". If it does then the code needs to compare if the Name in the previous row equals the Name in the current AND the Date of both rows equal each other.
If all these conditions are true the Hours worked should be summed up and the result should be copied to worksheet 2. Also the needed information like Name, Date and other information should be copied.
For now I tried this:
Sub check_Click()
Dim s1 As Worksheet, s2 As Worksheet
Dim N As Long, i As Long, p As Long
Set s1 = Sheets(1)
Set s2 = Sheets(2)
N = s1.Cells(s1.Rows.Count, "C").End(xlUp).Row
p = 1
For i = 1 To N
If IsNumeric(s1.Range("C" & i)) And s1.Cells(i, "C").Value < 10 Then
Next i
ElseIf s1.Cells(i, "B").Value = s1.Cells(i - 1, "B").Value And s1.Cells(i, "A").Value = s1.Cells(i - 1, "A").Value Then
s1.Range(Cells(i, "A"), Cells(i, "C")).Copy s2.Cells(p + 5, 1)
End If
End Sub
As you might see the code isn't working - unfortunate.
I hope someone can light my way.
The trickiest part is to compare the previous row and sum up the hours.
Thanks in advance
The code is not proper. Next i cannot be used inside If ... Then.
Because of lack continue in VBA you have to change condition also (or use Goto, but this is not my preferred solution):
Sub check_Click()
Dim s1 As Worksheet, s2 As Worksheet
Dim N As Long, i As Long, p As Long
Set s1 = Sheets(1)
Set s2 = Sheets(2)
N = s1.Cells(s1.Rows.Count, "C").End(xlUp).Row
p = 1
For i = 1 To N
If IsNumeric(s1.Range("C" & i)) And s1.Cells(i, "C").Value >= 10 Then
If s1.Cells(i, "B").Value = s1.Cells(i - 1, "B").Value And s1.Cells(i, "A").Value = s1.Cells(i - 1, "A").Value Then
s1.Range(Cells(i, "A"), Cells(i, "C")).Copy s2.Cells(p + 5, 1)
End If
End If
Next i
End Sub
EDIT:
Because values are compared with previous row, for loop neds to start from 2.
Sub check_Click()
Dim s1 As Worksheet, s2 As Worksheet
Dim N As Long, i As Long, p As Long
Set s1 = Sheets(1)
Set s2 = Sheets(2)
N = s1.Cells(s1.Rows.Count, "C").End(xlUp).Row
p = 1
For i = 2 To N ' Iterate from second row
If IsNumeric(s1.Range("C" & i)) And s1.Cells(i, "C").Value >= 10 Then
If s1.Cells(i, "B").Value = s1.Cells(i - 1, "B").Value And s1.Cells(i, "A").Value = s1.Cells(i - 1, "A").Value Then
s1.Range(Cells(i, "A"), Cells(i, "C")).Copy s2.Cells(p + 5, 1)
End If
End If
Next i
End Sub
Your Next i is in a wrong place. It should be after all the If statements.
I think comparing the values is done correctly.
If you have trouble copying hours summed just copy the entire row to sheet2 first and then separately update the hours worked cell with something like this:
Worksheets("sheet2").Cells(i,3).Value = Cells(i,3).Value + Cells(i-1,4).Value
Of course replace with the correct cell coordinates.
I'm trying to run a For Next Loop until the last row of a specific column (but not the last row of the sheet). So the first part of my list has data in column F and the second part doesn't. I only want the macro to apply to that first part. For some reason the loop only runs through the first part with certain commands but doesn't with the ones I am trying to do now. (I know it would be easy just to seperate the two parts manually and then run it but it drives me nuts not knowing what it is I did wrong :)).
This is the code:
Dim i As Integer
Dim g As Double
g = 0.083333333
Dim lastrow As Long
lastrow = Sheets("zm").Range("f" & Rows.Count).End(xlUp).Row
Sheets("zm").Activate
For i = 2 To lastrow
If Sheets("zm").Cells(i, 1) = Sheets("zm").Cells(i + 1, 1) And Sheets("zm").Cells(i, 5) = Sheets("zm").Cells(i + 1, 5) And Sheets("zm").Cells(i + 1, 6) - Sheets("zm").Cells(i, 7) < g Then
Sheets("zm").Cells(i + 1, 7).Copy
Sheets("zm").Cells(i, 7).Select
ActiveSheet.Paste
Application.CutCopyMode = False
Sheets("zm").Rows(i + 1).Delete
End If
Next i
Thanks for your help!
avoid Select/Selection and/or Activate/ActiveXXX
try this:
Option Explicit
Sub main()
Dim i As Long, lastrow As Long
Dim g As Double
g = 0.083333333
With Worksheets("zm")
lastrow = .Cells(.Rows.Count, "F").End(xlUp).Row
For i = lastrow To 2 Step -1
If .Cells(i, 1) = .Cells(i + 1, 1) And .Cells(i, 5) = .Cells(i + 1, 5) And .Cells(i + 1, 6) - .Cells(i, 7) < g Then
.Cells(i + 1, 7).Copy Destination:=.Cells(i, 7)
.Rows(i + 1).Delete
End If
Next i
End With
End Sub
I really need some help.
Problem: I have a workbook with 2 worksheets. Both sheets has headers. Sheet1 is a list of account numbers in column A and the same for sheet 2 column A. Now, what I need to do is this:
if I place a date in column AI in sheet 2 for a specific account number, then find the corresponding account number in sheet 1 and place the word "Complete" in column Y for that account.
I hope I explained this enough. Below is what I came up with so far, but got stuck:
Sub UpdateTBP()
Dim i
Dim j
Dim k
Dim LastRow
Dim LastRow2
LastRow = Sheets("Portfolio").Cells(Rows.Count, 1).End(xlUp).Row
LastRow2 = Sheets("TBP").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To (LastRow - 1)
For j = 2 To (LastRow2 - 1)
If Sheets("Portfolio").Cells(i, 1) = Sheets("TBP").Cells(j, 1).value Then
For k = 35 To 35
If Sheets("TBP").Cells(j, 35) <> "" Then
Sheets("Portfolio").Cells(i, Y).value = "Complete"
End If
Next
End If
Next
Next
ThisWorkbook.Save
End Sub
I was able to make it work by using the following modified code:
Sub UpdateTBP()
Dim i
Dim j
Dim k
Dim LastRow
Dim LastRow2
LastRow = Sheets("Portfolio").Cells(Rows.Count, 1).End(xlUp).Row
LastRow2 = Sheets("TBP").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To (LastRow - 1)
For j = 2 To (LastRow2 - 1)
If Sheets("Portfolio").Cells(i, 1).Value = Sheets("TBP").Cells(j, 1).Value Then
If Sheets("TBP").Cells(j, 35) <> "" Then
Sheets("Portfolio").Cells(i, 25).Value = "Complete"
End If
End If
Next
Next
ThisWorkbook.Save
End Sub
Please note that this will not include your last row of data as you have subtracted it out with:
For i = 2 To (LastRow - 1)
For j = 2 To (LastRow2 - 1)
If you wish to include that last row, just use the following:
For i = 2 to LastRow
For j = 2 to LastRow2
This includeds the previous comments I made. I just gave it a quick test and it is working.