Grab first and last occurence per group over multiple worksheets - excel

Problem description
I have a couple of worksheets showing open and close values per group (track).
All rows come with a date.
I want to loop through all worksheet and grab the oldest value for column Open and the most recent value for column Close.
Pseudo code:
Oldest and newest value per group for first worksheet
Per worksheet, grab the oldest value for Open and the most recent value for Close per group
Go to next worksheet and compare values
Next, go to the next worksheet and compare the oldest and new values with the previously captured ones. Per group, override the oldest value with the corresponding value in the current worksheet if the date in the current worksheet is older.
Override the most recent value with the corresponding value if the date in the current worksheet is more recent.
Repeat step 2 until we've looped through all worksheets.
I've been able to capture the oldest and most recent values per worksheet.
However, I can't figure out how to loop through all the worksheets and grab the oldest and most recent values per group over all worksheets.
I'm a starter on Excel VBA and want to stick with simple loops as per my current code. I want to loop through the worksheets "as is" meaning no renaming and no merging into one worksheet before running any code (there could be over a million rows in total).
Current code to grab the values per worksheet:
Sub top_one()
Dim WS As Worksheet
Dim group_start As Double
Dim track As String
Dim start_date, end_date As Long
Dim opening, closing As Double
For Each WS In ThisWorkbook.Worksheets
If WS.Name <> "1" And WS.Name <> "Expected" Then
WS.Select
With WS
LastRow = Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To LastRow
group_start = 2
If .Cells(i + 1, "A").Value <> .Cells(i, "A").Value Then
group_start = i - group_counter
track = .Cells(i, "A")
start_date = .Cells(group_start, "B")
opening = .Cells(group_start, "C")
end_date = .Cells(i, "B")
closing = .Cells(i, "D")
'lastRowTotal = Sheets("1").Cells(.Rows.Count, "P").End(xlUp).Row
Sheets("1").Cells(j + 2, "A") = .Cells(i, "A") 'trck
'If opening_date < Sheets("1").Cells(j + 2, "B") Then
Sheets("1").Cells(j + 2, "B") = opening_date
'Else
'End If
Sheets("1").Cells(j + 2, "B") = .Cells(group_start, "B") 'start date
Sheets("1").Cells(j + 2, "C") = .Cells(i, "B") 'end date
Sheets("1").Cells(j + 2, "D") = .Cells(group_start, "C") 'opening
Sheets("1").Cells(j + 2, "E") = .Cells(i, "D") 'closing
j = j + 1
group_counter = 0
Else
group_counter = group_counter + 1
End If
Next
j = 0
End With
End If
Next WS
End Sub
Screendumps
Worksheets data
Worksheet called 2018
Track Date Open Close
A 20180101 1 5
A 20180102 4 8
A 20180103 4 5
B 20180104 12 1
B 20180105 2 4
C 20180106 5 2
C 20180107 2 5
E 20180108 8 9
Worksheet called a
Track Date Open Close
A 20170101 5 6
A 20170102 6 6
B 20170103 2 1
B 20170104 1 2
C 20170105 5 9
C 20170106 9 7
D 20170107 5 5
D 20170108 5 8
D 20170109 7 2
Worksheet called 145jki
Track Date Open Close
A 20160101 8 5
A 20160102 4 5
B 20160103 11 5
B 20160104 8 9
C 20160105 10 3
C 20160106 5 7
Expected result
Track Start date End date First Open Last Close
A 20160101 20180103 8 5
B 20160103 20180105 11 4
C 20160105 20180107 10 5
D 20170107 20170109 5 2
E 20180108 20180108 8 9

Try this code
Sub Grab_First_Last_Occurence_Per_Group_Across_Worksheets()
Dim ws As Worksheet
Dim a() As Variant
Dim temp As Variant
Dim prev As Variant
Dim f As Boolean
Dim i As Long
Dim p As Long
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Worksheets
With ws
If .Name <> "1" And .Name <> "Expected" Then
temp = ws.Range("A2:D" & ws.Cells(Rows.Count, 1).End(xlUp).Row).Value
If f Then
a = ArrayJoin(a, temp)
Else
a = temp
f = True
End If
End If
End With
Next ws
BubbleSort a, 2
BubbleSort a, 1
ReDim b(1 To UBound(a, 1), 1 To 5)
For i = 1 To UBound(a, 1)
If a(i, 1) <> prev Then
p = p + 1
b(p, 1) = a(i, 1)
b(p, 2) = a(i, 2)
b(p, 3) = a(i, 2)
b(p, 4) = a(i, 3)
b(p, 5) = a(i, 4)
If p > 1 Then
b(p - 1, 3) = a(i - 1, 2)
b(p - 1, 5) = a(i - 1, 4)
End If
prev = a(i, 1)
End If
Next i
With Sheets("1")
.Range("A1").Resize(1, 5).Value = Array("Track", "Start Date", "End Date", "First Open", "Last Close")
.Range("A2").Resize(p, UBound(b, 2)).Value = b
End With
Application.ScreenUpdating = True
End Sub
Function ArrayJoin(ByVal a, ByVal b)
Dim i As Long
Dim ii As Long
Dim ub As Long
ub = UBound(a, 1)
a = Application.Transpose(a)
ReDim Preserve a(1 To UBound(a, 1), 1 To ub + UBound(b, 1))
a = Application.Transpose(a)
For i = LBound(b, 1) To UBound(b, 1)
For ii = 1 To UBound(b, 2)
a(ub + i, ii) = b(i, ii)
Next ii
Next i
ArrayJoin = a
End Function
Function BubbleSort(arr() As Variant, sortIndex As Long)
Dim b As Boolean
Dim i As Long
Dim j As Long
ReDim v(LBound(arr, 2) To UBound(arr, 2)) As Variant
Do
b = True
For i = LBound(arr) To UBound(arr) - 1
If arr(i, sortIndex) > arr(i + 1, sortIndex) Then
b = False
For j = LBound(v) To UBound(v)
v(j) = arr(i, j)
arr(i, j) = arr(i + 1, j)
arr(i + 1, j) = v(j)
Next
End If
Next i
Loop While Not b
End Function

Related

Converting weekly data in a table to monthly data using VBA

I have a table of hours against weeks (start of the week is a Sunday). The weekly data goes up to 12-16 months dependent on user input. I want to create a VBA macro which will iterate through this table of weekly hours data and convert the columns into monthly data.
Example:
All October 2021 related columns will collapse into 1 column called Oct-21. This will also combine the hours. 2nd row in the image below would equal 4+3+4+0= therefore value would be 11 in the new combined column's 2nd row.
My current thinking was calculating the Sundays between the start date and the last date which is below:
Dim d As Date, format As String, w As Long, FirstSunday As String
format = format(lastMonth, "Medium Date")
d = DateSerial(Year(format), Month(format), 1)
w = Weekday(d, vbSunday)
FirstSunday = d + IIf(w <> 1, 8 - w, 0)
Any ideas on how to do this?
Not sure how you want to group the weeks into months as some months will have 5 weeks. This code inserts a column when the month changes and then fills it with a sum formula for the relevant week columns. It assumes the dates are on row 1 , the task numbers in column 1 and the first week is in column 2.
Option Explicit
Sub ByMonth()
Dim wb As Workbook, ws As Worksheet
Dim LastCol As Long, LastRow As Long, c As Long, n As Long
Dim dt As Date
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet1")
LastCol = ws.Cells(1, Columns.Count).End(xlToLeft).Column
LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
' scan cols from right to left insert new columns
Application.ScreenUpdating = False
For c = LastCol + 1 To 3 Step -1
' add columns on month change
If Month(ws.Cells(1, c)) <> Month(ws.Cells(1, c - 1)) Then
ws.Columns(c).Insert
With ws.Columns(c)
.HorizontalAlignment = xlCenter
'.Interior.Color = RGB(255, 255, 200)
.Font.Bold = True
.Cells(1).NumberFormat = "#"
End With
End If
Next
' scan left to right filling new cols with sum() formula
' hide weekly columns
LastCol = ws.Cells(1, Columns.Count).End(xlToLeft).Column
n = 0
For c = 2 To LastCol + 1
If ws.Cells(1, c) = "" Then
dt = ws.Cells(1, c - 1)
ws.Cells(1, c) = MonthName(Month(dt), True) & " " & Year(dt)
ws.Cells(2, c).Resize(LastRow - 1).FormulaR1C1 = "=SUM(RC[-" & n & "]:RC[-1])"
n = 0
Else
ws.Columns(c).EntireColumn.Hidden = True
n = n + 1
End If
Next
' copy visible month columns to sheet2
ws.Cells.SpecialCells(xlCellTypeVisible).Copy
With wb.Sheets("Sheet2")
.Activate
.Range("A1").PasteSpecial xlPasteValues
.Range("A1").Select
End With
' end
ws.Columns.Hidden = False
Application.ScreenUpdating = True
Application.CutCopyMode = False
MsgBox "Done"
End Sub
Please, try the next code. It assumes that in column A:A, starting from the 6th row, there are (not sorted) tasks. If they are sorted, the code will run without problem, too. It uses arrays and a dictionary and mostly working in memory, should be very fast for big ranges:
Sub SumWeeksMonths()
Dim sh As Worksheet, sh1 As Worksheet, lastR As Long, arrWk, arrMonths, arrTasks
Dim i As Long, k As Long, j As Long, El, arr, arrFin, dict As New Scripting.Dictionary
Set sh = ActiveSheet 'use there the sheet to be processed
Set sh1 = sh.Next 'use here the sheet where the processed result to be returned
lastR = sh.Range("A" & sh.rows.count).End(xlUp).row 'last row (in column A:A)
arrWk = sh.Range(sh.Range("B5"), sh.cells(5, sh.Columns.count).End(xlToLeft)).Value 'place the Week headers in a 2D array
ReDim arrMonths(UBound(arrWk, 2) - 1)'redim the 1D array to keep the unique munths, at a maximum size
For i = 1 To UBound(arrWk, 2) - 1 'create the array of (only) months:
If month(DateValue(arrWk(1, i))) <> month(DateValue(arrWk(1, i + 1))) Then
k = k + 1: arrMonths(k) = Format(DateValue(arrWk(1, i + 1)), "mmm-yyyy")
Else
arrMonths(k) = Format(DateValue(arrWk(1, i)), "mmm-yyyy")
End If
Next i
ReDim Preserve arrMonths(k) 'preserve only the existing Date elements
For Each El In sh.Range("A4:A" & lastR).Value
dict(El) = 1 'extract the unique tasks (only to count them for ReDim the necessary array)
Next El
'place all the range to be processed in an array (for faster iteration):
arr = sh.Range("A5", sh.cells(lastR, sh.cells(5, sh.Columns.count).End(xlToLeft).Column)).Value
ReDim arrFin(1 To UBound(dict.Keys) + 1, 1 To UBound(arrMonths) + 2) 'reDim the final array to keep processed data
ReDim arrTasks(UBound(arrMonths)) 'redim the array to temporarily keep the array of each task summ
dict.RemoveAll: k = 0 'clear the dictionary and reitinialize the K variable
For i = 2 To UBound(arr) 'iterate between the main array elements:
If Not dict.Exists(arr(i, 1)) Then 'if the Task key does not exist:
For Each El In arrMonths 'iterate between each month in arrMonths:
For j = 2 To UBound(arr, 2) 'iterate between all arr columns for the i row:
If month(DateValue(arr(1, j))) = month(El) Then 'if column months is a specific arrMonths column:
arrTasks(k) = arrTasks(k) + arr(i, j) 'sumarize everything in the arrTask each element
End If
Next j
k = k + 1 'increment k, for the next month
Next El
dict.Add arr(i, 1), arrTasks 'create the dictionary key with the tasks array as item
ReDim arrTasks(UBound(arrMonths)): k = 0 'reinitialize arrTasks and k variable
Else 'if dictionary (task) key exists:
For Each El In arrMonths
For j = 2 To UBound(arr, 2)
If month(DateValue(arr(1, j))) = month(El) Then
arrTasks(k) = dict(arr(i, 1))(k) + arr(i, j) 'add the sum to the allready existing elements
End If
Next j
k = k + 1
Next El
dict(arr(i, 1)) = arrTasks 'make the item the updaded array
ReDim arrTasks(UBound(arrMonths)): k = 0 'reinitialize arrTasks and k variable
End If
Next i
'place the processed values in final array (arrFin):
For i = 0 To UBound(arrMonths) 'firstly the headers:
arrFin(1, i + 2) = arrMonths(i)
Next i
'Extract the tasks value for each month and place in the final array appropriate columns:
For i = 0 To dict.count - 1 'iterate between the dictionary elements:
arrFin(i + 2, 1) = dict.Keys(i) 'place the task in the array first column, starting from the second row
For j = 0 To UBound(dict.items(i)) 'iterate between the dictionary item array elements
arrFin(i + 2, j + 2) = dict.items(i)(j) 'place the appropriate array elements in the final array (arrFin)
Next j
Next i
'drop the final array at once and make some formatting:
With sh1.Range("A1").Resize(UBound(arrFin), UBound(arrFin, 2))
.Value = arrFin
With .rows(1)
.Font.Bold = True
.Interior.ColorIndex = 20
.BorderAround 1
End With
.EntireColumn.AutoFit
.BorderAround 1
End With
sh1.Activate 'to see the processing result...
MsgBox "Ready..."
End Sub
Please, test it and send some feedback.

Insert a row n times

I´ve an Excel file with 10 Columns. In columns 2, 3, 4 I have a number or a dash.
If the sum of these 3 cells is greater than 1, I need to replace that entire row with n rows that have only one of the columns with the value 1 but the other cells stay the same.
Example
1 - - #-> leave it as is
- 2 - #-> replace that row with 2 rows : - 1 - ; - 1 -
2 - 1 #-> replace that row with 3 rows : 1 - - ; 1 - - ; - - 1;
I managed to iterate from bottom up, but I´m having trouble storing a row in memory, manipulate it and insert below.
Sub Test()
Dim rng As Range
Dim count20, count40, count45, total, i As Integer
Set rng = Range("A3", Range("A3").End(xlDown))
For i = rng.Cells.count To 1 Step -1
count20 = 0
count40 = 0
count45 = 0
total = 0
count20 = Cells(rng.Item(i).Row, 10).Value
If count20 > 1 Then
total = total + count20
End If
count40 = Cells(rng.Item(i).Row, 11).Value
If count40 > 1 Then
total = total + count40
End If
count45 = Cells(rng.Item(i).Row, 12).Value
If count45 > 1 Then
total = total + count45
End If
If total <> 0 Then
MsgBox total
End If
Next i
End Sub
EDIT 2
I’ve provided alternative code based on your latest comment. It uses columns J-L (10-12) as the numeric cells to be changed, and columns A-I (1-9) and M-AD (13-30) as the cells with text to be preserved. As before, sheet 1 starting in row 3 is assumed, and you can change this to whatever you need.
Option Explicit
Sub testJtoL()
Dim LastRow As Long, i As Long, j As Long, c As Long, _
insertR As Long, TopRow As Long, BottomRow As Long
Dim b As Range
Dim ws As Worksheet
'*** This code is based your values being in Columns J-L (10-12) in sheet 1 ***
'Set sheet 1 as ws
Set ws = Sheet1
'Sheet1 column J is used here to get your last row
LastRow = ws.Cells(Rows.Count, 10).End(xlUp).Row
'*** This code is based your values starting in Row 3 ***
For c = LastRow To 3 Step -1
'Determine number of rows to insert based on sum of that row
insertR = Application.WorksheetFunction.Sum(Range(Cells(c, 10), Cells(c, 12))) - 1
If insertR = 0 Then GoTo skip
'STEP 1 insert the correct number of rows
With ws.Range(Cells(c + 1, 1), Cells(c + insertR, 30))
.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
End With
'STEP 2 fill the values into the correct number of rows
insertR = insertR + 1
With ws.Range(Cells(c, 1), Cells(c, 30))
.Resize(insertR, 30).Value = .Value
End With
TopRow = c
If insertR = 0 And c = 3 Then
BottomRow = c
Else
BottomRow = c + insertR - 1
End If
'STEP 3 replace all numbers with 1 or "-"
'Replace numbers in column J
If ws.Range(Cells(c, 10), Cells(c, 10)).Value = "-" Then GoTo SkipA
i = ws.Range(Cells(c, 10), Cells(c, 10)).Value
j = 1
For Each b In ws.Range(Cells(TopRow, 10), Cells(BottomRow, 10))
If j <= i Then
b.Value = 1
b.Offset(0, 1).Value = "-"
b.Offset(0, 2).Value = "-"
Else
b.Value = "-"
End If
j = j + 1
Next b
SkipA:
'Replace numbers in column K
j = 1
For Each b In ws.Range(Cells(TopRow, 11), Cells(BottomRow, 11))
If b.Value = "-" Then GoTo SkipB
i = b.Value
If j <= i Then
b.Value = 1
b.Offset(0, 1).Value = "-"
Else
b.Value = "-"
End If
j = j + 1
SkipB:
Next b
'Replace numbers in column L
j = 1
For Each b In ws.Range(Cells(TopRow, 12), Cells(BottomRow, 12))
If b.Value = "-" Then GoTo SkipC
i = b.Value
If j <= i Then
b.Value = 1
Else
b.Value = "-"
End If
j = j + 1
SkipC:
Next b
skip:
Next c
End Sub

Increasing time difference between data points

I'm trying to make a macro that increases the time between data points as part of automatic data processing, but it currently takes way too long.
One of my sensors logs a data point every 10 seconds, I want to increase this dt to 1 hour. For this I wrote some very simple (inefficient) code (see below) that does work but takes 10-40 minutes to process 1 week of data which is far from ideal.
I've seen recommendations for semi-similar issues to use an array, however I have 0 experience with this and don't know if it's applicable to this goal.
Do While Cells(row + 1, 2).Value <> ""
If Cells(row + 1, 2).Value - Cells(row, 2).Value < 1 / 24.05 Then
Rows(row + 1).Select
Selection.Delete Shift:=xlUp
Else
row = row + 1
End If
Loop
EDIT:
I solved my issue with a slightly edited version of #Damian's code as shown below.
Sub Change_dt()
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim target As Single
target = Sheets("Controller").Cells(16, 9).Value
Dim arrSource As Variant
With ThisWorkbook.Sheets("Raw data")
arrSource = .UsedRange.Value 'this will input the whole used sheet inside the array
Dim finalArr As Variant
ReDim finalArr(1 To UBound(arrSource), 1 To UBound(arrSource, 2))
.Cells.Delete 'will clean the worksheet
Dim i As Long, x As Long, j As Long, Z As Long
x = 1
Z = 1
For i = 1 To UBound(arrSource)
On Error Resume Next
If arrSource(i + Z, 1) = vbNullString Or i = UBound(arrSource) Then Exit For 'will end the loop once the next row is empty
On Error GoTo 0
'If the next row substracted the first is greater than target both will be copied to the final array
If arrSource(i + Z, 1) - arrSource(i, 1) > target Then
For j = 1 To UBound(arrSource, 2)
finalArr(x, j) = arrSource(i, j)
finalArr(x + 1, j) = arrSource(i + Z, j)
Next j
x = x + 2 'increment 2 on x because you wrote 2 lines
i = i + Z
Z = 1
Else
Z = Z + 1
End If
Next i
'paste the resulting array back to the sheet
.Range("A1", .Cells(UBound(finalArr), UBound(finalArr, 2))).Value = finalArr
'eliminate the extra unused rows
i = .Cells(.Rows.Count, 1).End(xlUp).row + 1
.Rows(i & ":" & .Rows.Count).Delete
End With
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
This should help a lot in your executing time:
Sub Change_dt()
Dim target As Single
target = Sheets("Controller").Cells(16, 9).Value
Dim arrSource As Variant
With ThisWorkbook.Sheets("Raw data")
arrSource = .UsedRange.Value 'this will input the whole used sheet inside the array
Dim finalArr As Variant
ReDim finalArr(1 To UBound(arrSource), 1 To UBound(arrSource, 2))
.Cells.Delete 'will clean the worksheet
Dim i As Long, x As Long, j As Long
x = 1
For i = 5 To UBound(arrSource)
On Error Resume Next
If arrSource(i + 1, 2) = vbNullString Or i = UBound(arrSource) Then Exit For 'will end the loop once the next row is empty
On Error GoTo 0
'If the next row substracted the first is greater than 1/24.05 both will be copied to the final array
If Not arrSource(i + 1, 2) - arrSource(i, 2) < target Then
For j = 1 To UBound(arrSource, 2)
finalArr(x, j) = arrSource(i, j)
finalArr(x + 1, j) = arrSource(i + 1, j)
Next j
x = x + 2 'increment 2 on x because you wrote 2 lines
End If
Next i
'paste the resulting array back to the sheet
.Range("A1", .Cells(UBound(finalArr), UBound(finalArr, 2))).Value = finalArr
'eliminate the extra unused rows
i = .Cells(.Rows.Count, 1).End(xlUp).row + 1
.Rows(i & ":" & .Rows.Count).Delete
End With
End Sub

How to use loop to SUM categories?

I am trying to use a loop with vba to sum values from one worksheet to another. I am struggling with writing my code to match values from Sheet 4 and if the value matches then sum the categories from Sheet 1, if not then skip to the next office. I would also like to exclude certain categories from being included in the SUM loop for example, exclude "Book". Currently my macro is writing to Sheet3. Here is my code:
Option Explicit
Sub test()
Dim a, i As Long, ii As Long, dic As Object
Set dic = CreateObject("Scripting.Dictionary")
dic.CompareMode = 1
a = Sheets("sheet1").Cells(1).CurrentRegion.Value
With CreateObject("Scripting.Dictionary")
.CompareMode = 1
For i = 2 To UBound(a, 1)
If Not dic.Exists(a(i, 1)) Then dic(a(i, 2)) = dic.Count + 2
If Not .Exists(a(i, 1)) Then
Set .Item(a(i, 1)) = CreateObject("Scripting.Dictionary")
.Item(a(i, 1)).CompareMode = 1
End If
.Item(a(i, 1))(a(i, 2)) = .Item(a(i, 1))(a(i, 2)) + a(i, 3)
Next
ReDim a(1 To .Count + 1, 1 To dic.Count + 1)
a(1, 1) = Sheets("sheet1").[a1]
For i = 0 To dic.Count - 1
a(1, i + 2) = dic.Keys()(i)
Next
For i = 0 To .Count - 1
a(i + 2, 1) = .Keys()(i)
For ii = 2 To UBound(a, 2)
a(i + 2, ii) = .items()(i)(a(1, ii)) + 0
Next
Next
End With
With Sheets("sheet3").Cells(1).Resize(UBound(a, 1), UBound(a, 2))
.EntireColumn.ClearContents
Sheets("sheet1").[a1].Copy .Rows(1)
.Value = a: .Columns.AutoFit: .Parent.Activate
End With
End Sub
This is how the data looks
and this is the output that is desired
In this example, we will use arrays to achieve what you want. I have commented the code so that you shall not have a problem understanding it. However if you still do then simply ask :)
Input
Output
Logic
Find last row and last column of input sheet
Store in an array
Get unique names from Column A and Row 1
Define output array
Compare array to store sum
Create new sheet and output to that sheet
Code
Option Explicit
Sub Sample()
Dim ws As Worksheet, wsNew As Worksheet
Dim tempArray As Variant, OutputAr() As Variant
Dim officeCol As New Collection
Dim productCol As New Collection
Dim itm As Variant
Dim lrow As Long, lcol As Long, totalsum As Long
Dim i As Long, j As Long, k As Long
'~~> Input sheet
Set ws = Sheet1
With ws
'~~> Get Last Row and last column
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
lcol = .Cells(1, Columns.Count).End(xlToLeft).Column
'~~> Store it in a temp array
tempArray = .Range(.Cells(2, 1), .Cells(lrow, lcol)).Value
'~~> Create a unique collection using On error resume next
On Error Resume Next
For i = LBound(tempArray) To UBound(tempArray)
officeCol.Add tempArray(i, 1), CStr(tempArray(i, 1))
productCol.Add tempArray(i, 2), CStr(tempArray(i, 2))
Next i
On Error GoTo 0
End With
'~~> Define you new array which will hold the desired output
ReDim OutputAr(1 To officeCol.Count + 1, 1 To productCol.Count + 1)
'~~> Store the rows and columns in the array
i = 2
For Each itm In officeCol
OutputAr(i, 1) = itm
i = i + 1
Next itm
i = 2
For Each itm In productCol
OutputAr(1, i) = itm
i = i + 1
Next itm
'~~> Calculate sum by comparing the arrays
For i = 2 To officeCol.Count + 1
For j = 2 To productCol.Count + 1
totalsum = 0
For k = LBound(tempArray) To UBound(tempArray)
If OutputAr(i, 1) = tempArray(k, 1) And _
OutputAr(1, j) = tempArray(k, 2) Then
totalsum = totalsum + tempArray(k, 3)
End If
Next k
OutputAr(i, j) = totalsum
Next j
Next i
'~~> Create a new sheet
Set wsNew = ThisWorkbook.Sheets.Add
'~~> Outout the array
wsNew.Range("A1").Resize(officeCol.Count + 1, productCol.Count + 1).Value = OutputAr
End Sub

How to duplicate preferred columns data in Conditionally one sheet to multiple sheets

In My office five Employee is working for example In my office Employ Entry Exit sheet is dere..
This is Main Sheet
Now my requirement
category wise data copy to this sheet to other sheet but it's do it automatically
Like Example
enter image description here
I hope I am interpreting your question correctly, but please let me know if I have misinterpreted your request.
Try the following code on your sheet:
Sub AutoCopyByName()
Dim Names() As String
Dim i As Long, NumRows As Long, NameRow() As Long
Dim j As Integer, NumNames As Integer
j = 0
NumSites = 0
'''''''''''''''''''''''''''''''''''''''''''
'''COUNT NUMBER OF ROWS WITH INFORMATION'''
'''''''''''''''''''''''''''''''''''''''''''
i = 2 'Standard Counter (counts all non-blank cells)
NumRows = 1 'Number of rows with information
Do While WorksheetFunction.IsText(Sheets("data").Range("A" & i))
If Sheets("data").Range("A" & i) <> " " Then NumRows = NumRows + 1
i = i + 1
Loop
'''''''''''''''''''''''''''
'''COUNT NUMBER OF NAMES'''
'''''''''''''''''''''''''''
For i = 3 To NumRows + 1
If Sheets("data").Cells(i, 1) <> Sheets("data").Cells(i - 1, 1) Then NumNames = NumNames + 1 'Works
Next i
''''''''''''''''''
'''REDIM ARRAYS'''
''''''''''''''''''
ReDim Names(NumNames)
ReDim NameRow(NumNames)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''FINDING THE LOCATION OF EACH NAME IN THE SHEET AND STORING IT IN NameRow ARRAY'''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
For i = 2 To NumRows + 1
If Sheets("data").Cells(i, 1) <> Sheets("data").Cells(i - 1, 1) Then
Names(j) = Sheets("data").Cells(i, 1).Value
NameRow(j) = i
j = j + 1
End If
Next i
'''''''''''''''''''''''''''''''''''''''''
'''COPY ENTRIES PER NAME TO EACH SHEET'''
'''''''''''''''''''''''''''''''''''''''''
For i = 0 To NumNames - 1
Worksheets.Add
Worksheets(1).Name = Names(i)
Worksheets("data").Rows(1).Copy
Worksheets(Names(i)).Paste
Worksheets("data").Activate
Worksheets("data").Range(Cells(NameRow(i), 1), Cells(NameRow(i + 1) - 1, 1)).EntireRow.Copy
Worksheets(Names(i)).Activate
Worksheets(Names(i)).Range("A2").Select
Worksheets(Names(i)).Paste
Next i
End Sub
I've used the following as my input sheet

Resources