I am trying to delete row based upon their values (i.e. if a cell contains the word DELETE) then the entire row should be deleted and shifted up.
I currently have code that loops through data and applies the cell value "IN-SCOPE" or "DELETE" to column 11 depending on the date present in Column 4. This works fine - however, the code I've written to delete any items labeled with "DELETE" doesn't do anything. Below is the code I currently have - any help would be great.
'Loop that lables items as in-scope IF they fall within the user defined range
y = 2
StartDate = Controls.Cells(15, 10).Value
EndDate = Controls.Cells(15, 11).Value
Bracknell.Activate
Cells(1, 11).Value2 = "Scope Check"
Do While Cells(y, 4).Value <> ""
If Cells(y, 9).Value >= StartDate And Cells(y, 9).Value < EndDate Then
Cells(y, 11).Value = "IN-SCOPE"
Else: Cells(y, 11).Value = "DELETE"
End If
y = y + 1
Loop
'Loop to delete out of scope items
Bracknell.Activate
z = 1
Do While Cells(z, 4).Value <> ""
If Cells(z, 11).Value = "DELETE" Then
Range("A" & z).EntireRow.Delete shift:=xlUp
End If
z = z + 1
Loop
Try this, the code is self explained:
Option Explicit
'use option explicit to force yourself
'to declare all your variables
Sub Test()
'Loop that lables items as in-scope IF they fall within the user defined range
Dim StartDate As Date
StartDate = Controls.Cells(15, 10).Value
Dim EndDate As Date
EndDate = Controls.Cells(15, 11).Value
With Bracknell
'Instead deleting every row, store them into a range variable
Dim RangeToDelete As Range
'Calculate your last row with data
Dim LastRow As Long
'Assuming your column 4 has data on all the rows
'If not, change that 4 for a column index that has data.
LastRow = .Cells(.Rows.Count, 4).End(xlUp).Row
'The most efficient way to loop through cells
'is using For Each loop
Dim cell As Range
.Cells(1, 11) = "Scope Check"
'loop through every row in column 4
For Each cell In .Range(.Cells(2, 4), .Cells(LastRow, 4))
'if the cell of that row in column 9 is between
If .Cells(cell.Row, 9) >= StartDate And .Cells(cell.Row, 9) < EndDate Then
.Cells(cell.Row, 11) = "IN-SCOPE"
Else
'if not, check if rangetodelete is empty
If RangeToDelete Is Nothing Then
'if it is empty, set it as the cell
Set RangeToDelete = cell
Else
'if not, set it as what it already is and the new cell
Set RangeToDelete = Union(RangeToDelete, cell)
End If
End If
Next cell
'Once you ended the loop you'll get the variable
'with every cell that didn't meet your criteria
'Check if is nothing, which means there are no cell to delete
If Not RangeToDelete Is Nothing Then RangeToDelete.EntireRow.Delete
End With
End Sub
Related
I would like populate the blue area with random numbers.
sum of C3 to R3 should be equal to B3 value: 124
also;
sum of C3 to C26 should be equal to C2 value: 705
I tried to achieve it with the following code:
(this code was originally posted here: Code by #Mech
Sub RandomNumbersArray()
' dim your variables. this tells vba what type of variable it is working with
Dim lRow As Long
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets("SPLIT BY DAYS")
' find the last row in column b (2) in the above defined ws
lRow = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row
' loop through rows 3 to last row
For i = 3 To lRow
' generate a random number between 0 and the row contents of column B (5)
ws.Cells(i, 3).Value = Int(Rnd() * (ws.Cells(i, 2).Value + 1))
' generate a random number between 0 and the difference between column B and colum C
ws.Cells(i, 4).Value = Int(Rnd() * (ws.Cells(i, 2).Value - ws.Cells(i, 3).Value))
' subtract the difference between column B and the sum of column C and column D
ws.Cells(i, 5).Value = ws.Cells(i, 2).Value - (ws.Cells(i, 3).Value + ws.Cells(i, 4).Value)
' subtract the difference between column B and the sum of column C and column D and column E
ws.Cells(i, 6).Value = ws.Cells(i, 2).Value - (ws.Cells(i, 3).Value + ws.Cells(i, 4).Value + ws.Cells(i, 5).Value)
' subtract the difference between column B and the sum of column C and column D and column E and column F
ws.Cells(i, 7).Value = ws.Cells(i, 2).Value - (ws.Cells(i, 3).Value + ws.Cells(i, 4).Value + ws.Cells(i, 5).Value + ws.Cells(i, 6).Value)
Next i
' sum column C (column 3) and place the value in C2
ws.Cells(2, 3).Value = Application.WorksheetFunction.Sum(Range(Cells(3, 3), Cells(lRow, 3)))
' sum column D (column 4) and place the value in D2
ws.Cells(2, 4).Value = Application.WorksheetFunction.Sum(Range(Cells(3, 4), Cells(lRow, 4)))
' sum column E (column 5) and place the value in E2
ws.Cells(2, 5).Value = Application.WorksheetFunction.Sum(Range(Cells(3, 5), Cells(lRow, 5)))
' sum column F (column 6) and place the value in F2
ws.Cells(2, 6).Value = Application.WorksheetFunction.Sum(Range(Cells(3, 6), Cells(lRow, 6)))
' sum column G (column 7) and place the value in F2
ws.Cells(2, 7).Value = Application.WorksheetFunction.Sum(Range(Cells(3, 7), Cells(lRow, 7)))
End Sub
EDIT: Just to clarify, no negative numbers.
Here is something to try:
Set all cells to 0. Create a list of all cells (some kind of reference to each cell).
Now, randomly choose a cell from your list, and add 1 to that cell. The very first time, all cells will be 0, except for one, which will now be 1.
For this cell that you just incremented, add up the row and column and see if the sums have been reached. If either the row or the column sum has been reached, remove this cell reference from the list.
Repeat (randomly choose a cell from those remaining on the list) until the list is empty.
At each iteration you are randomly choosing one of the remaining cells in the reference list (not choosing from all the cells) and this list is getting smaller and smaller as column or row sums are reached.
It should be the case that random cells will increment, and if the columns and sums can in fact be calculated by values without logical inconsistencies, you should fairly quickly reach that point when the reference list falls empty.
I have a solution.
Answers so far have mostly been about finding values which are random, then fixing them to fit the totals.
I tried finding a calculated (non random) solution that fits the totals, then made a separate sub to randomize it. This way you can prevent the randomization from introducing negative values.
There are two procedures, This sub will call them both on the same Range.
Sub Call_Random_Array
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets("SPLIT BY DAYS")
Dim RangeToFill as Range: Set RangeToFill = ws.Range("C3:R26") 'Edit this line to select whatever range you need to fill randomly
'Proportionately fill the array to fit totals:
Call ProportionateFillArray(RangeToFill)
'Randomize it x times
For x = 1 to 10 'increase this number for more randomisation
Call RandomizeValues(RangeToFill)
Next
End Sub
Proportionately fill the array to fit totals:
Sub ProportionateFillArray(rngAddress As Range)
Dim ws As Worksheet: Set ws = rngAddress.Worksheet
'Horizontal and Vertical target values as ranges:
Dim hTarg As Range, vTarg As Range
Set hTarg = rngAddress.Rows(1).Offset(-1, 0)
Set vTarg = rngAddress.Columns(1).Offset(0, -1)
'Check the totals match
If Not WorksheetFunction.Sum(hTarg) = WorksheetFunction.Sum(vTarg) Then
'totals don't match
MsgBox "Change the targets so both the horizontal and vertical targets add up to the same number."
Exit Sub
End If
With rngAddress
'Now fill rows and columns with integers
Dim Row As Long, Col As Long
For Row = 1 To .Rows.Count
For Col = 1 To .Columns.Count
.Cells(Row, Col) = Int( _
hTarg.Cells(Col) * vTarg.Cells(Row) / WorksheetFunction.Sum(hTarg) _
)
Next
Next
'Correct rounding errors
For Row = 1 To .Rows.Count
For Col = 1 To .Columns.Count
If Row = .Rows.Count Then
'Last row, so this column must be corrected come what may
.Cells(Row, Col) = .Cells(Row, Col) - WorksheetFunction.Sum(.Columns(Col)) + hTarg.Cells(Col)
ElseIf Col = .Columns.Count Then
'Last column, so must be corrected come what may
.Cells(Row, Col) = .Cells(Row, Col) - WorksheetFunction.Sum(.Rows(Row)) + vTarg.Cells(Row)
ElseIf _
(WorksheetFunction.Sum(.Rows(Row)) - vTarg.Cells(Row)) * _
(WorksheetFunction.Sum(.Columns(Col)) - hTarg.Cells(Col)) > 0 Then
'both row and column are incorrect in the same direction
.Cells(Row, Col) = .Cells(Row, Col) - WorksheetFunction.Max( _
WorksheetFunction.Sum(.Rows(Row)) - vTarg.Cells(Row), _
WorksheetFunction.Sum(.Columns(Col)) - hTarg.Cells(Col))
End If
Next
Next
End With
End Sub
Randomize an array without changing row or column totals:
Sub RandomizeValues(rngAddress As Range)
Dim ws As Worksheet: Set ws = rngAddress.Worksheet
Dim rngIncrease(1 To 2) As Range, rngDecrease(1 To 2) As Range, lDiff As Long
With rngAddress
'Select two cells to increase at random
For a = 1 To 2
Set rngIncrease(a) = .Cells(RndIntegerBetween(1, .Rows.Count), RndIntegerBetween(1, .Columns.Count))
rngIncrease(a).Select
Next
'Corresponding cells to decrease to make totals the same:
Set rngDecrease(1) = ws.Cells(rngIncrease(1).Row, rngIncrease(2).Column)
Set rngDecrease(2) = ws.Cells(rngIncrease(2).Row, rngIncrease(1).Column)
'Set the value to increase/decrease by - can't be more than the smallest rngDecrease Value, to prevent negative values
If Not WorksheetFunction.Min(rngDecrease) > 1 Then
'Don't decrease a value below 1
Exit Sub
Else
lDiff = RndIntegerBetween(1, WorksheetFunction.Min(rngDecrease)-1)
End If
'Now apply the edits
For a = 1 To 2
rngIncrease(a) = rngIncrease(a) + lDiff
rngDecrease(a) = rngDecrease(a) - lDiff
Next
End With
End Sub
'The below is the Random Integer function, I also used it in my other answer
Function RndIntegerBetween(Min As Long, Max As Long) As Long
RndIntegerBetween = Int((Max - Min + 1) * Rnd + Min)
End Function
This code is for what you were trying to do, not exactly how you explained it though (see comments). If this is what you were looking for, then your explanation was a bit off, otherwise let me know what you did mean.
Sub RandomNumbersArray()
Dim lRow As Long, lColumn As Long, remainingValue As Long
Dim wb As Workbook: Set wb = ActiveWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets("SPLIT BY DAYS")
lRow = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row
lColumn = ws.Cells(2, ws.Columns.Count).End(xlToLeft).Column
For i = 3 To lRow 'loop through the rows
remainingValue = ws.Cells(i, 2).Value2
For j = 3 To lColumn 'loop through all the columns per row
' generate a random number between 0 and the row contents of column B - previous column
If j = lColumn Then 'last cell can't be random unless you want to extend the columns until the sum in B-column is met
ws.Cells(i, j).Value2 = remainingValue
Else
ws.Cells(i, j).Value2 = Int((remainingValue + 1) * Rnd)
End If
remainingValue = remainingValue - ws.Cells(i, j).Value2
Next j
Next i
For j = 3 To lColumn 'loop through the columns to set the sum
ws.Cells(2, j).Value2 = Application.WorksheetFunction.Sum(Range(Cells(3, j), Cells(lRow, j)))
Next j
End Sub
I'm yet to get past the O-column with any value above 0 however
I have one problem that I need to solve and is as following. I would like to insert data into cells if conditions are met. Conditions are from cells M1 to M3 and this cells are in dropdown list.
Cell M1 is column B:B
Cell M2 is column C:C
Cell M3 is column D:D
Cell M4 is typed manualy
Cell M5 is a number and is also typed manualy(randoml)
Once I defined from dropdown cells M1 to M3 and type some text in cell M4 and type a number in cell M5, program should insert in column H cell M4, in column I program should automaticaly insert today date and in column J sould automaticaly insert actual time. In how many cells should be this inserted is defined in cell M5. If in some part of the table, row are reserved, macro should skip this already inserted data
If from the table there is no free/empty cells, then the program should inform a user with a notification
Could you please help me to create a macro to automaticaly insert data into cells
Thank you
I try this in attached table, but unfirtunately, results are not this what I expected
Sub code_res()
Dim lr As Long, r As Long
Dim i As Integer
Dim iSheet As Worksheet
Dim ans As Integer
Set iSheet = Worksheets("List1")
lr = Cells(Rows.Count, "A").End(xlUp).Row
i = 0
Cells(4, 14) = 0
For r = 7 To lr
If Cells(r, 2) = Cells(1, 13) And Cells(r, 3) = Cells(2, 13) And Cells(r, 7) = "" Then
Cells(r, 7) = Cells(3, 13)
Cells(r, 9) = Date
With Cells(r, 10)
.Value = Time
.NumberFormat = "hh:mm:ss"
i = i + 1
If i = Cells(4, 13) Then
Exit For
End If
End With
End If
Next r
iSheet.Range("M4") =
iSheet.Application.WorksheetFunction.CountIf(iSheet.Range("H:H"),
iSheet.Range("M3"))
'testing quantity and export
If Cells(4, 13) > Cells(4, 14) Then
ans = MsgBox("Quantity of reservation " & Cells(4, 14), vbQuestion + vbYesNo,"Do you want to do a export?")
If ans = vbYes Then
Call Izvoz1 'data export
Else
MsgBox "Data are not exported"
End If
Else
Call Izvoz1 'data export
MsgBox "Succesesfully exported"
End If
End Sub
I am trying to have an entry data userform where the input starts at "A19" and will end at row "A30" once the empty rows get filled 1 by 1, instead of the current situation where it starts at "A1" and goes unrestricted.
Private Sub cmdAdd_Click()
Dim wks As Worksheet
Dim AddNew As Range
Set wks = ActiveSheet
Set AddNew = wks.Range("A2:A65565").End(xlUp).Offset(1, 0)
AddNew.Offset(0, 2).Value = txtCAC.Text
AddNew.Offset(0, 4).Value = txtName.Text
AddNew.Offset(0, 5).Value = txtType.Text
AddNew.Offset(0, 6).Value = txtClass.Text
AddNew.Offset(0, 7).Value = txtDate.Text
AddNew.Offset(0, 8).Value = txtParent.Text
AddNew.Offset(0, 9).Value = txtManagement.Text
AddNew.Offset(0, 10).Value = txtSuccess.Text
AddNew.Offset(0, 12).Value = txtPercentage.Text
AddNew.Offset(0, 21).Value = txtCommittment.Text
AddNew.Offset(0, 38).Value = txtContribution.Text
AddNew.Offset(0, 40).Value = txtRedemption.Text
lstDisplay.ColumnCount = 41
lstDisplay.RowSource = "A2:A65356"
End Sub
As apparently your goal is to overwrite an adjacent fixed target range
with a complete set of textbox entries, I'd propose the following steps:
[0.] Define the fixed start cell in target cell e.g. via set tgt = Sheet1.Range("A19").
[1. a)] Split a list of needed textbox names thus getting a 1-dimensional array, which btw will be 0-based automatically.
[1. b)] Provide for data by a 2-dim data array and make it zero-based, too in order to
synchronize both array counters in the following loop (1.c).
[1. c)] Fill the data array rows with all textbox contents by a For..Next loop
and check for not allowed zero-length inputs;
if there are any display a warning message and redirect focus to the empty textbox.
[2.] Eventually dump back the data array to the chosen target range
by using the number of listed textbox controls cnt to .Resize the target range (e.g. 12 in OP).
Private Sub cmdAdd_Click()
'0. Define fixed start cell in target range
dim tgt as Range
set tgt = Sheet1.Range("A19") ' change to any wanted sheet Code(Name)
'1. a) split a list of needed textbox names getting a 1-dim 0-based array automatically
Dim myTextboxes As Variant
myTextboxes = Split( _
"txtCAC,txtName,txtType,txtClass,txtDate,txtParent,txtManagement," & _
"txtSuccess,txtPercentage,txtCommittment,txtContribution,TxtRedemption", _
",")
Dim cnt As Long
cnt = UBound(myTextboxes) + 1 ' count number of textboxes
' b) provide for data by a 2-dim data array (make it zero-based, too)
Dim data As Variant
ReDim data(0 To cnt - 1, 0 To 0) ' define dimensions holding data
' c) fill data array rows with all textbox contents
Dim i As Long, ctrl As MSForms.Control
For i = LBound(data) To UBound(data) ' i.e. 0 To 11
Set ctrl = Me.Controls(myTextboxes(i)) ' set control to memory
data(i, 0) = ctrl.Text ' get textbox content
' check for complete entries or exit sub
If Len(Trim(data(i, 0))) = 0 Then ' check for zero-length input
MsgBox "Fill in empty Textbox(es) first!", vbExclamation, ctrl.Name
ctrl.SetFocus ' set focus to empty box
Exit Sub ' escape procedure
End If
Next
'2. dump data to target range
tgt.Resize(cnt, 1) = data ' write data
End Sub
Further hints
I think there will be to need to define a RowSource (btw better to use "Sheet1!A19:A30" if you are overwriting all data anyway by command button.
Side note: Prefer to get a last row cell via e.g. Sheet1.Range("A" & .Rows.Count).End(xlUp) or the row index via .Range("A" & .Rows.Count).End(xlUp).Row instead of coding a fixed rows count (current sheets have at about 1 million). You might be interested in reading Finding last used cell
`
Dim wks As Worksheet
Dim AddNew As Range
Set wks = ActiveSheet
Dim h As Integer
h = wks.Range("C65356").End(xlUp).Row
If h < 18 Then h = 18
if h > 30 then exit Sub
Set AddNew = wks.Range("A" & h)
AddNew.Offset(1, 2).Value = txtCAC.Text
AddNew.Offset(1, 4).Value = txtName.Text
AddNew.Offset(1, 5).Value = txtType.Text
AddNew.Offset(1, 6).Value = txtClass.Text
AddNew.Offset(1, 7).Value = txtDate.Text
AddNew.Offset(1, 8).Value = txtParent.Text
AddNew.Offset(1, 9).Value = txtManagement.Text
AddNew.Offset(1, 10).Value = txtSuccess.Text
AddNew.Offset(1, 12).Value = txtPercentage.Text
AddNew.Offset(1, 21).Value = txtCommittment.Text
AddNew.Offset(1, 38).Value = txtContribution.Text
AddNew.Offset(1, 40).Value = txtRedemption.Text
'lstDisplay.ColumnCount = 41
'lstDisplay.RowSource = "A2:A65356"
Trying to loop two columns and put result into one column.
1) looping is incorrect (no hits = wrong)
2) printing puts result into two different columns ("O" +7 from H and "R" +7 from K).
Private Sub FindValueKH_JN()
'New column O (no 15)
'Find if value starting in column H (no8) is between 207100-208100
'AND if value starting in column K (no11) is between 12700-12729,
' then T2J in column O, else T2N in O
Range("O1").Select
Selection.EntireColumn.Insert , CopyOrigin:=xlFormatFromLeftOrAbove
ActiveCell.FormulaR1C1 = "T2 er Ja eller Nei"
Dim loopRange As Range
'From H to new column O is +7 columns
lastrow1 = ActiveSheet.Cells(Rows.Count, "H").End(xlUp).Row
'From K to new column O is +4 columns
lastrow2 = ActiveSheet.Cells(Rows.Count, "K").End(xlUp).Row
'loop columns H and K
Set loopRange = Union(Range("H2:H" & lastrow1), Range("K2:K" & lastrow2))
For Each cell In loopRange
If Left(cell.Value, 6) >= 207100 And Left(cell.Value, 6) <= 208100 And _
Left(cell.Value, 5) >= 12700 And Left(cell.Value, 5) <= 12729 Then
cell.Offset(0, 7).Value = "T2J"
Else: cell.Offset(0, 7).Value = "T2N"
End If
Next cell
End Sub
Your references are incorrect, and this is why you are not getting any hits. You want to check two separate columns for specific values, but instead are just looking in one single cell for both conditions:
For Each cell In loopRange will loop through every cell in your defined loopRange range, which contains both columns.
You'd have to change your code so it loops through just a single column instead, like the following
Dim loopRange As Range
lastrow = ActiveSheet.Cells(Rows.Count, "H").End(xlUp).Row 'From H to new column O is +7 columns
Set loopRange = Range("H2:H" & lastrow1) 'loop columns H
For Each cell In loopRange
If Left(cell.Value, 6) >= 207100 And Left(cell.Value, 6) <= 208100 And Left(cell.Offset(, 3).Value, 5) >= 12700 And Left(cell.Offset(, 3).Value, 5) <= 12729 Then
cell.Offset(0, 7).Value = "T2J"
Else: cell.Offset(0, 7).Value = "T2N"
End If
Next cell
In your If-statement, you are checking the content of a single cell and your If-statement can never be true. With your Union-statement, you will get a Range with all cells of Col H and all cells of Col K, and in the loop you are checking all cells that are either in H or in K.
So your If hits, for example, Cell H2 and you are checking if the content is > 207100 and in the same moment < 12729.
What you probably want is to loop over all cells if column H, check it's value together with the value of the cell in column K of the same row.
I assume your cells contain a string starting with a number but holds also some characters. I would advice that you write the values into intermediate variables, makes it much easier to debug. You are using the left-function which will give you the first 6 (resp. 5) characters. The result is still a string (even if it contains only digits), and you compare it to a number, and that's not a good idea because now VBA has to do some implicit conversions, and that may lead to unexpected results. You should use the Val-function to convert a string into a numeric value.
As already mentioned in the comments, never work implicit on the so called Active Worksheet. Specify explicitly the worksheet you want to work with.
One question: Why do you use the strange syntax for the Else-statement. The : means that you put a second statement into a line. It is much more readable to omit the : and put the next statement(s) into separate lines.
Dim loopRange As Range, cell As Range, lastrow As Long
With ThisWorkbook.Sheets(1)
lastrow = .Cells(Rows.Count, "H").End(xlUp).row
Set loopRange = .Range("H2:H" & lastrow)
End With
For Each cell In loopRange
Dim valH As Long, valK As Long
valH = Val(Left(cell.Value, 6))
valK = Val(Left(cell.Offset(0, 3).Value, 6))
If valH >= 207100 And valH <= 208100 And valK >= 12700 And valK <= 12729 Then
cell.Offset(0, 7).Value = "T2J"
Else
cell.Offset(0, 7).Value = "T2N"
End If
Next cell
I am very new to writing in VBA and am struggling with the following task.
I have a workbook with multiple sheets with a large set of data in each (10000 rows). I am able to remove the data I don't need quite easily and can sort the data. I am left with column 1 - a list of Parts, and columns 4 and 5 - Planned Time and Actual Time.
What I want to do with this data is to find the average of columns 4 and 5 for each unique value in column 1. I thought it would be easiest to do the following
Loop for each worksheet
Sort the data for "Part"
Create a variable array
Loop for each row
If the previous row "Part" is the same as the current row then add the that row's "Planned Time" and "Actual Time" to the variable array
If the previous row "Part" is different calculate the average of data in the variable array
Output the averages to a Results sheet with their unique "Part"
Any help would be appreciated. Mainly how to work with the variable array and how to perform the check to fill the array. Thank you.
Mark,
I've put this VBA macro togther for you which should do the trick. The script will loop through all your worksheets and summarise the info into an array (ask you asked). The array is then output into the results table.
Note : You will need to ensure that your workbook contains a sheet called "Results". The script will output the details you need to the "Results" sheet.
Option Explicit
Sub getResults()
'set variables
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Dim ii As Long
Dim partName As String
'set array to contain the parts/avarage data
Dim partsAverageArray() As Variant
ReDim partsAverageArray(1 To 4, 1 To 1)
'loop through each sheet in the workbook
For Each ws In ActiveWorkbook.Sheets
'ignore worksheet if it's name is "Results"
If Not ws.Name = "Results" Then
'get last row in the sheet using column A (size of the table of parts)
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
'loop down the table of parts data starting at row 2 (assuming that row 1 contains the heading of the columns
i = 2
For i = 2 To lastRow
'get the part name
partName = ws.Cells(i, 1).Value
'check if the part does/does not exist within the array yet
'loop through the array to get this info
'check if array has any info in it yet
If partsAverageArray(1, 1) = "" Then
'array is blank so add the first part
'add part name
partsAverageArray(1, 1) = partName
'part occurences
partsAverageArray(2, 1) = 1
'sum of time planned
partsAverageArray(3, 1) = ws.Cells(i, 4).Value
'sum of time taken (actual)
partsAverageArray(4, 1) = ws.Cells(i, 5).Value
Else
'array already exists so loop through it looking for a part match
ii = 1
For ii = 1 To UBound(partsAverageArray, 2)
'test for a part match
If partsAverageArray(1, ii) = partName Then
'match found
'so add/cumulate data into the array
'part occurences (add 1)
partsAverageArray(2, ii) = partsAverageArray(2, ii) + 1
'sum of time planned (total)
partsAverageArray(3, ii) = partsAverageArray(3, ii) + ws.Cells(i, 4).Value
'sum of time taken (actual) (total)
partsAverageArray(4, ii) = partsAverageArray(4, ii) + ws.Cells(i, 5).Value
'stop the loop of the array
ii = UBound(partsAverageArray, 2)
Else
'part name does not match
'check if the end of the array has been reached
If ii = UBound(partsAverageArray, 2) Then
'the end of the array has been reached and the part not found
'therefore add an additional dimension to the array and put the part's details into it
ReDim Preserve partsAverageArray(1 To 4, 1 To (UBound(partsAverageArray, 2) + 1))
'add part name
partsAverageArray(1, UBound(partsAverageArray, 2)) = partName
'part occurences
partsAverageArray(2, UBound(partsAverageArray, 2)) = 1
'sum of time planned
partsAverageArray(3, UBound(partsAverageArray, 2)) = ws.Cells(i, 4).Value
'sum of time taken (actual)
partsAverageArray(4, UBound(partsAverageArray, 2)) = ws.Cells(i, 5).Value
'stop the loop of the array
ii = UBound(partsAverageArray, 2)
Else
'part name has not been found and the array has not looped to the end.
'therefore keep the array looping and do nothing
End If
End If
Next ii
End If
Next i
End If
Next ws
'--------------------------------------------------------
'output data from the array to the reults sheet
'--------------------------------------------------------
Set ws = Sheets("Results")
'set the results table headings
ws.Cells(1, 1).Value = "Part"
ws.Cells(1, 2).Value = "Part Count"
ws.Cells(1, 3).Value = "Planned Time (Average)"
ws.Cells(1, 4).Value = "Actual Time (Average)"
'clear the old results from the table before adding the new results
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
ws.Range("A2:D" & lastRow).ClearContents
i = 1
For i = 1 To UBound(partsAverageArray, 2)
'part name
ws.Cells(i + 1, 1).Value = partsAverageArray(1, i)
'part count
ws.Cells(i + 1, 2).Value = partsAverageArray(2, i)
'average (planned)
ws.Cells(i + 1, 3).Value = partsAverageArray(3, i) / partsAverageArray(2, i)
'average (actual)
ws.Cells(i + 1, 4).Value = partsAverageArray(4, i) / partsAverageArray(2, i)
Next i
'view results
ws.Activate
End Sub
Hope this helps!