GroupBy and Sum rows using multiple columns in Excel - excel

I am trying to groupby and sum the rows of an Excel file and sum the amount.
Sample Data:
Name
Age
NetPay
Gross Value
Manz
36
260
1200
Nerz
26
760
1480
Manz
36
140
1290
Nerz
26
160
1495
Manz
36
880
1140
Manz
16
260
1200
Kiks
24
470
1700
Rats
31
290
1760
Manz
36
260
1200
Expected Output:
Name
Age
NetPay
Gross Value
Manz
36
1540
4830
Nerz
26
920
2975
Manz
16
260
1200
Kiks
24
470
1700
Rats
31
290
1760
Sub Output_Final_Validation(strval As String)
Dim wrkbokval As Workbook
Dim shtval As Worksheet
Dim shterror As Worksheet
Dim Lastrow As Long
Dim LastCol As Long
Set wrkbokval = Workbooks.Open(strval)
Set shtval = wrkbokPRval.Sheets("Sheet4")
Lastrow = shtval.Cells(Rows.Count, 1).End(xlUp).Row
shtval.Range("A2:AP" & Lastrow).Value = shtval.Range("A2:Z" & Lastrow).Value
Please suggest how we can resolve it,
I need to groupby on column Name and Age and ̀ Sum NetPayandGross Value`.
I have tried using Pivot as well but couldn't worked.

You can do it using a Dictionary to store name concatenaded with age (it looks like it's your unique ID, but consider using something else because 2 people can share name AND age easily) and then apply SumIfs to get your calculus:
Sub test()
Dim rngSource As Range
Dim i As Long
Dim Dict As Object
Dim LR As Long
Dim MyStr As Long
LR = Range("A" & Rows.Count).End(xlUp).Row
Set Dict = CreateObject("Scripting.Dictionary")
Set rngSource = Range("A2:D" & LR)
For i = 2 To LR Step 1
If Dict.Exists(Range("A" & i).Value & "|" & Range("B" & i).Value) = False Then
Dict.Add Range("A" & i).Value & "|" & Range("B" & i).Value, 0
End If
Next i
'destination. I'm pasting in cell G2
Range("G2").Resize(Dict.Count, 1) = Application.WorksheetFunction.Transpose(Dict.Keys)
Dict.RemoveAll
Set Dict = Nothing
i = 2
Do Until Range("G" & i).Value = ""
Range("H" & i).Value = Split(Range("G" & i).Value, "|")(1) 'age
Range("G" & i).Value = Split(Range("G" & i).Value, "|")(0) 'name
With Application.WorksheetFunction
Range("I" & i).Value = .SumIfs(rngSource.Columns(3), rngSource.Columns(1), Range("G" & i).Value, rngSource.Columns(2), Range("H" & i).Value) 'NetPay
Range("J" & i).Value = .SumIfs(rngSource.Columns(4), rngSource.Columns(1), Range("G" & i).Value, rngSource.Columns(2), Range("H" & i).Value) 'Gross Value
End With
i = i + 1
Loop
Set rngSource = Nothing
End Sub

Related

Transposed varying Row data to Columns using VBA?

Attached is XLSM (VBA) for transposing rows to columns.
Transpose Varying rows to columns.
If the data is consistent Use TransposeRows the number of columns to be copied and transposed.
If the number of rows for each set is varying then use the TransposeRows2 procedure.
' Please note the code checks the Font color for the end of the record and transposes them to columns so If you need
' anything other than the color Maybe a specific word like 'end' then it can be used instead of the font color.
Sub TransposeRows()
' Convert Rows to Columns specify the range in this case it is 9 rows offset
Dim rng As Range
Dim i As Long
Dim MyRange As Range
Dim lngLastRow As Long
lngLastRow = Sheet1.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
MsgBox lngLastRow
Set rng = Sheet1.Range("A1")
i = 1
J = 9
Do While rng.Value <> ""
rng.Resize(J).Copy
Sheet2.Range("A" & i).PasteSpecial Transpose:=True
Set rng = rng.Offset(J)
'MsgBox Sheet2.Range("A" & i).Font.ColorIndex
i = i + 1
Loop
Application.CutCopyMode = False
End Sub
Sub TransposeRows2()
' Transpose Varying rows to columns.
' Please note the code checks the Font color for end of the record and transposes them to columns so If you need
' anything other than the color like say a specific word like end then it can be used instead of the font color.
Dim rng As Range
Dim i As Long
Dim MyRange As Range
Dim lngLastRow As Long
lngLastRow = Sheet1.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'MsgBox lngLastRow
Set rng = Sheet1.Range("A1")
i = 1
J = 0
K = 1
J1 = 0
F = 0
Do While rng.Value <> ""
Do Until Sheet1.Range("A" & K).Font.ColorIndex <> 49 And Sheet1.Range("A" & K).Font.ColorIndex <> 16 And Sheet1.Range("A" & K).Font.ColorIndex <> 50 And Sheet1.Range("A" & K).Font.ColorIndex <> 46 And Sheet1.Range("A" & K).Font.ColorIndex <> 55 'And (Sheet1.Range("A" & K).Font.ColorIndex = 50 Or Sheet1.Range("A" & K).Font.ColorIndex = 16)
K = K + 1
Loop
F = F + J
J = K - F
' K = K + 1
J1 = J
If Sheet1.Range("A" & K).Font.ColorIndex <> 49 Then
'If Sheet1.Range("A" & K + 1).Font.ColorIndex = 16 And Sheet1.Range("A" & K + 1).Font.ColorIndex = 18 And Sheet1.Range("A" & K).Font.ColorIndex <> 49 Then
K = K + 1
End If
rng.Resize(J).Copy
If Sheet1.Range("A" & K).Font.ColorIndex = 18 Then 'Or Sheet1.Range("A" & K).Font.ColorIndex = 16 Or Sheet1.Range("A" & K).Font.ColorIndex = 50 Then
'J = 0
K = K + 1
End If
Sheet3.Range("A" & i).PasteSpecial Transpose:=True
Set rng = rng.Offset(J)
i = i + 1
Loop
Application.CutCopyMode = False
End Sub
But how would we do it for varying rows like one set being 9 rows and another being 16 rows and so on?
Sub TransposeRows2()
Dim rng As Range
Dim i As Long
Dim MyRange As Range
Dim lngLastRow As Long
lngLastRow = Sheet1.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'MsgBox lngLastRow
Set rng = Sheet1.Range("A1")
i = 1
J = 0
K = 1
J1 = 0
F = 0
Do While rng.Value <> ""
Do Until Sheet1.Range("A" & K).Font.ColorIndex <> 49 And Sheet1.Range("A" & K).Font.ColorIndex <> 16 And Sheet1.Range("A" & K).Font.ColorIndex <> 50 And Sheet1.Range("A" & K).Font.ColorIndex <> 46 And Sheet1.Range("A" & K).Font.ColorIndex <> 55 'And (Sheet1.Range("A" & K).Font.ColorIndex = 50 Or Sheet1.Range("A" & K).Font.ColorIndex = 16)
K = K + 1
Loop
F = F + J
J = K - F
' K = K + 1
J1 = J
If Sheet1.Range("A" & K).Font.ColorIndex <> 49 Then
'If Sheet1.Range("A" & K + 1).Font.ColorIndex = 16 And Sheet1.Range("A" & K + 1).Font.ColorIndex = 18 And Sheet1.Range("A" & K).Font.ColorIndex <> 49 Then
K = K + 1
End If
rng.Resize(J).Copy
If Sheet1.Range("A" & K).Font.ColorIndex = 18 Then 'Or Sheet1.Range("A" & K).Font.ColorIndex = 16 Or Sheet1.Range("A" & K).Font.ColorIndex = 50 Then
'J = 0
K = K + 1
End If
Sheet3.Range("A" & i).PasteSpecial Transpose:=True
Set rng = rng.Offset(J)
i = i + 1
Loop
Application.CutCopyMode = False
End Sub

VBA How to omit blank cells and avoid returning 00/01/1900?

The AE, AG, AH, whenever the D or AD parallel cells are empty, return e.g. date of 00/01/1900 or time as 00:00. Can you please clarify how to return blank if the same parallel cell in D or AD is blank? Thanks
Sub valuedifference()
Dim Total As Double
Dim TimeX As Date
Dim TimeY As Date
Dim LastRow As Long
Dim i As Long
With ThisWorkbook.Sheets("Test1")
LastRow = .Cells(.Rows.Count, "d").End(xlUp).Row
For i = 2 To LastRow
TimeX = CDate(.Range("d" & i).Value)
TimeY = CDate(.Range("ad" & i).Value)
Total = TimeValue(TimeY) - TimeValue(TimeX)
.Range("ae" & i).Value = Total
.Range("ag" & i).Value = Abs(Total * 24)
.Range("ah" & i).Value = Abs(Total * 1440)
Next i
End With
End Sub
I think this a formatting issue- If TimeY is "" and TimeX is "", then
Total = TimeValue(TimeY) - TimeValue(TimeX) is 0.
0 in time format is 0:0:00 and 0 in date format is 00/01/1900
One solution to this is to include an if statement that checks that there are not blank cells
For i = 2 To LastRow
If .Range("D" & i).Value <> "" And .Range("AD" & i).Value <> "" Then
TimeX = CDate(.Range("d" & i).Value)
TimeY = CDate(.Range("ad" & i).Value)
Total = DateDiff("n", TimeY, TimeX)
.Range("AE" & i).Value = Total
.Range("AG" & i).Value = Format(Abs(Total), "#.##")
.Range("AH" & i).Value = Format(Abs(Total), "#.##")
End If
Next i

change background colour for partial row dependant on 1 Cell

So I'm at the last hurdle with my VBA coding. I'm creating a schedule for several different countries and need the background for cells A7:H300 to colour automatically depending on a value within the same specific row being the country code.
I know I could use conditional formatting but the colours do not copy and paste into a separate sheet using that method.
The code I have below works but it colours D:K instead of the expected A:H - The value is in Row D so I'm guessing that's the problem but I can't workout a way around it.
Thank you for you help :)
Sub ChangeColour()
Set PC = Range("A:H")
For Each cell In PC
If cell.Value = "BEZEE" Then cell.Columns("A:H").Interior.ColorIndex = 40
If cell.Value = "BEANR" Then cell.Columns("A:H").Interior.ColorIndex = 40
If cell.Value = "DEBRH" Then cell.Columns("A:H").Interior.ColorIndex = 37
If cell.Value = "FRLEH" Then cell.Columns("A:H").Interior.ColorIndex = 38
If cell.Value = "GBBRS" Then cell.Columns("A:H").Interior.ColorIndex = 35
If cell.Value = "GBLPL" Then cell.Columns("A:H").Interior.ColorIndex = 35
If cell.Value = "GBSOU" Then cell.Columns("A:H").Interior.ColorIndex = 35
If cell.Value = "NLRTM" Then cell.Columns("A:H").Interior.ColorIndex = 40
If cell.Value = "FIHNO" Then cell.Columns("A:H").Interior.ColorIndex = 36
If cell.Value = "SEGOT" Then cell.Columns("A:H").Interior.ColorIndex = 36
If cell.Value = "ZADUR" Then cell.Columns("A:H").Interior.ColorIndex = 45
If cell.Value = "ZAELS" Then cell.Columns("A:H").Interior.ColorIndex = 45
If cell.Value = "ZAPLZ" Then cell.Columns("A:H").Interior.ColorIndex = 45
Next
End Sub
You're addressing the wrong range. The way you're attempting to do it effectively acts as an Offset from the referenced Cell. A better of way of writing it as well would be the following:
Public Sub ChangeColour()
Dim PC As Range, LastRow As Range
Dim ColorIndexValue As Long
Dim cell
' Set your desired range - Should reference Relevant worksheet as well
Set PC = Range("A7:H1000")
' Find last used row in that range - This will help limit the number of loops on a fixed range and speed up execution
Set LastRow = PC.Find(what:="*", _
after:=Cells(PC.Row, PC.Column), _
lookat:=xlWhole, _
LookIn:=xlValues, _
searchorder:=xlByRows, _
searchdirection:=xlPrevious)
If Not LastRow Is Nothing Then
' Resize PC to actual used range instead of working on entire sheet
Set PC = PC.Cells(1).Resize(LastRow.Row, PC.Columns.Count)
' Loop through all cells in range in Column D
For Each cell In PC.Columns("D").Cells
' Set ColorIndexValue variable based on cell value
Select Case cell.Value2
Case "GBBRS", "GBLPL", "GBSOU": ColorIndexValue = 35
Case "FIHNO", "SEGOT": ColorIndexValue = 36
Case "BEANR", "DEBRH": ColorIndexValue = 37
Case "FRLEH": ColorIndexValue = 38
Case "BEZEE", "NLRTM": ColorIndexValue = 40
Case "ZADUR", "ZAELS", "ZAPLZ": ColorIndexValue = 45
Case Else: ColorIndexValue = 0
End Select
' Set cell Color. Skip 0 as assume cell is 0 by default
If ColorIndexValue > 0 Then
' Calculates applicable range from cell and PC context
With Range(cell.Offset(0, PC.Cells(1).Column - cell.Column), cell.Offset(0, PC.Cells(1, PC.Columns.Count).Column - cell.Column))
.Interior.ColorIndex = ColorIndexValue
End With
End If
Next cell
End If
End Sub
I think you could try:
Option Explicit
Sub test()
Dim Lastrow As Long, i As Long
With ThisWorkbook.Worksheets("Sheet1")
Lastrow = .Cells(.Rows.Count, "D").End(xlUp).Row
For i = 1 To Lastrow
If .Range("D" & i).Value = "BEZEE" Or .Range("D" & i).Value = "BEANR" Or .Range("D" & i).Value = "NLRTM" Then
.Range("A" & i & ":H" & i).Interior.ColorIndex = 40
ElseIf .Range("D" & i).Value = "DEBRH" Then
.Range("A" & i & ":H" & i).Interior.ColorIndex = 37
ElseIf .Range("D" & i).Value = "FRLEH" Then
.Range("A" & i & ":H" & i).Interior.ColorIndex = 38
ElseIf .Range("D" & i).Value = "GBBRS" Or .Range("D" & i).Value = "GBLPL" Or .Range("D" & i).Value = "GBSOU" Then
.Range("A" & i & ":H" & i).Interior.ColorIndex = 35
ElseIf .Range("D" & i).Value = "FIHNO" Or .Range("D" & i).Value = "SEGOT" Then
.Range("A" & i & ":H" & i).Interior.ColorIndex = 36
ElseIf .Range("D" & i).Value = "ZADUR" Or .Range("D" & i).Value = "ZAELS" Or .Range("D" & i).Value = "ZAPLZ" Then
.Range("A" & i & ":H" & i).Interior.ColorIndex = 45
End If
Next i
End With

How do I copy/paste cell values if the condition is true and enter "0" if the cell being looked at in the condition isn't there?

I'm very new to VBA and I haven't been able to figure this out yet.
I need a code that copies cell .Range("E" & i) to cell Sheets("CANmonitor").Range("C" & k) if Column B = 1731.
However, it needs to look at .Range("C" & i) as well and set Sheets("CANmonitor").Range("C" & K) = 0 if .Range("C" & i) is between 6 & 16 or between 28 & 39.
To simplify, the value on column C jumps from 6 to 16 and from 28 to 39. I need the code to enter 0s for the missing values (ex: 1731.6 = data from cell ("E" & i) on sheet DIC2; 1731.7 = 0). Here's what I have so far:
Sub DIC2toCAN()
Dim LR As Long, i As Long, k As Long
With Sheets("DIC2")
LR = .Range("B" & Rows.Count).End(xlUp).Row
k = 1
For i = 1 To LR
With .Range("B" & i)
If .Value = "1731" Then
If .Range("C" & i) is between 6 & 16 or is between 28 & 39 Then
Sheets("CANmonitor").Range("C" & k) = 0
Else
Sheets("DIC2").Range("E" & i).Copy _
Destination:=Sheets("CANmonitor").Range("C" & k)
k = k + 1
End If
End If
End With
Next i
End With
End Sub
Here is another version. This code declares all the objects beforehand and then works with it. This way if later you need to change the sheet name, you do it at only one place. This code (Untested) also uses Select Case for simplicity.
Sub DIC2toCAN()
Dim LR As Long, i As Long, k As Long
Dim wsI As Worksheet, wsO As Worksheet
'~~> Set your sheets here
Set wsI = ThisWorkbook.Sheets("DIC2")
Set wsO = ThisWorkbook.Sheets("CANmonitor")
With wsI
LR = .Range("B" & Rows.Count).End(xlUp).Row
k = 1
For i = 1 To LR
'~~> Use Val to get the value
If Val(.Range("B" & i).Value) = 1731 Then
'~~> Use select case for simplicity
Select Case Val(.Range("C" & i).Value)
Case 6 To 16, 28 To 39
wsO.Range("C" & k).Value = 0
Case Else
.Range("E" & i).Copy wsO.Range("C" & k)
k = k + 1
End Select
End If
Next i
End With
End Sub
I cleaned up your code just a bit to correct a few things (formatting, missing dot, removed problematic With statement), and most importantly, I added the syntax for the "between" tests that you needed.
Sub DIC2toCAN()
Dim LR As Long, i As Long, k As Long
With Sheets("DIC2")
LR = .Range("B" & .Rows.Count).End(xlUp).Row
k = 1
For i = 1 To LR
If .Range("B" & i).Value = "1731" Then
Dim test As Variant
test = .Range("C" & i).Value
If (test >= 6 And test <= 16) Or (test >= 28 And test <= 39) Then
Sheets("CANmonitor").Range("C" & k) = 0
Else
Sheets("DIC2").Range("E" & i).Copy Destination:=Sheets("CANmonitor").Range("C" & k)
k = k + 1
End If
End If
Next i
End With
End Sub
You are very, very close.
Change this:
If .Range("C" & i) is between 6 & 16 or is between 28 & 39 Then
to
If (.Range("C" & i) >= 6 and .Range("C" & i) <= 16) OR _
(.Range("C" & i) >= 28 and .Range("C" & i) <=39) Then
Also, simplify this:
Sheets("DIC2").Range("E" & i).Copy Destination:=Sheets("CANmonitor").Range("C" & k)
to this:
Sheets("CANmonitor").Range("C" & k) = Sheets("DIC2").Range("E" & i)
It gives the same result, but requires less typing and is easier to read.

How to match rows that have 2 separately matching columns

I have 4 columns, A through D. I need to find rows where column C is the same in each row, and column D is the same in each row. It would be best to have a True or False value placed in column E. VBA or a formula works, although I'd think something like this is do-able with a formula.
For example, I have the following:
Row 1 XX 123 XYZ
Row 2 XX 234 XYZ
Row 3 XX 234 YZX
Row 4 XX 234 YZX
In this example, Column E would be False for Rows 1 and 2 and True for Rows 3 and 4.
Is this what you are trying?
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, i As Long
Set ws = Sheets("Sheet1")
With ws
lRow = .Range("C" & .Rows.Count).End(xlUp).Row
For i = 1 To lRow
If i = 1 Then
If .Range("C" & i).Value = .Range("C" & i + 1).Value And _
.Range("D" & i).Value = .Range("D" & i + 1).Value Then _
.Range("E" & i).Value = "True" Else .Range("E" & i).Value = "False"
Else
If (.Range("C" & i).Value = .Range("C" & i + 1).Value And _
.Range("D" & i).Value = .Range("D" & i + 1).Value) Or _
(.Range("C" & i).Value = .Range("C" & i - 1).Value And _
.Range("D" & i).Value = .Range("D" & i - 1).Value) Then _
.Range("E" & i).Value = "True" Else .Range("E" & i).Value = "False"
End If
Next i
End With
End Sub
SNAPSHOT

Resources