Create a checkpoint in a foreach statement - excel

I am writing a code that put an X in a cell depending on a offset cell value, for exemple if the offset cell has a value of 3, it will put an X in the cell and decrement the offset cell value, i want to save the location of that cell and start the next for each with it.
For Each Cell In plage
If (Cell.Offset(0, 1).Value <> 0) Then
If (Cell.Value <> "X") Then
Cell.Offset(0, 1).Value = Cell.Offset(0, 1).Value - 1
Cell.Value = "X"
Checkpoint = Cell.Address
Exit For
Else
Cell.Value = ""
GoTo NextStep
End If
Exit For
Else
Cell.Value = ""
End If
NextStep:
Next Cell
The problem i am having with the current code is it start the loop all over again while i want it to keep till the end of the lines, until all offset value are equal to 0.

Try the below (there are notes on the code). If you face difficulties let me know.
Option Explicit
Sub test()
'In this example we assume that the data you want to loop appear in Column A
Dim i As Long, Lastrow As Long
Dim Checkpoint As Variant
With ThisWorkbook.Worksheets("Sheet1") '<- Change sheet name if needed
Lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row '< -Fins the lastrow of the column you want to loop
For i = 2 To Lastrow ' < -Start looping from row 2 to Lastrow fo the column
If .Range("A" & i).Offset(0, 1).Value <> 0 Then '<- You are looping
If .Range("A" & i).Value <> "X" Then
.Range("A" & i).Offset(0, 1).Value = .Range("A" & i).Offset(0, 1).Value - 1
.Range("A" & i).Value = .Range("A" & i).Value & "X"
Checkpoint = .Range("A" & i).Address
Else
.Range("A" & i).Value = ""
End If
Else
.Range("A" & i).Value = ""
End If
Next i
End With
End Sub

Is plage a range?
If so, you could update it to start from the checkpoint and include all cells up to some lastCell for example.
Something like:
set plage=thisWorkbook.Worksheets("Your Worksheet").Range(checkpoint,lastCell)
That way the next For-Each should start from your checkpoint.
BTW if I understand correctly what you'e trying to do, I would suggest you replace cell.value="" with cell.clearContents

Related

To Get only one raw of result for a particular Range

Following code is suggested by a helpful user, this works well to Calculate "From", "To", "MAX" etc values of a range. But this code gives results in every row of a range. I want to get the results in only first row of each row. Please help with this.
Sub trial()
Dim lastrow As Long
Dim destSht As Worksheet
Dim i As Long, j As Long
Set destSht = Worksheets("Final")
With Worksheets("Source")
lastrow = .Range("B" & .Rows.Count).End(xlUp).Row
For i = 2 To lastrow + 1 'loop whole range (column C)
If .Cells(i, "C") <> "" Then 'If column C is not empty then
For j = i To lastrow + 1 'Loop "group" range to find next empty cell. Start from current loop i to last row and add one row to get to next empty cell.
If .Cells(j, "C") = "" Then 'When next empty cell is found (i.e. end of small group range) then apply formulas
.Cells(i, "E").Value = .Cells(i, "B").Value 'From
.Cells(i, "F").Value = .Cells(j - 1, "B").Value 'To
.Cells(i, "G").Formula = "=MAX(C" & i & ":C" & j - 1 & ")" 'MAX
.Cells(i, "H").Formula = "=MIN(C" & i & ":C" & j - 1 & ")" 'MIN
.Cells(i, "I").Formula = "=AVERAGE(C" & i & ":C" & j - 1 & ")" 'AVG
Exit For
End If
Next j
End If
Next I
End With
End Sub
This Code gives following result
Desired Result
Try changing this line:
If .Cells(i, "C") <> "" Then 'If column C is not empty then
To this line:
If .Cells(i, "C") <> "" AND .Cells(i-1, "C") = "" Then 'If column C is not empty AND the column C above is empty then

Selection of Continued filled Cells and Calculation of MAX,MIN,AVG

Hope You are all Safe
I'm trying to calculate MAX, MIN and AVG Values of filled cells which are continued without blank cell (As you can see it in the left side of the sample image ).
I'm facing problem in selecting these randomly placed cells and calculate the above values and also "From" and "To" values of respective range.
Please let me know how to do it. So far I've constructed following code
Dim Cel As Range
Dim lastrow As Long
Dim destSht As Worksheet
Set destSht = Worksheets("Final")
With Worksheets("Source")
lastrow = .Range("B" & .Rows.Count).End(xlUp).Row
For Each Cel In .Range("C2:C" & lastrow)
If .Cells(Cel.Row, "C") <> "" Then
Cel.Offset(0, -1).Copy Destination:=destSht.Cells(destSht.Rows.Count, 1).End(xlUp).Offset(0, 1)
'It will give "From" Column
'' Plz suggest for "To" Column
Range("G5").Select
ActiveCell.FormulaR1C1 = "=MAX(RC[-4]:R[4]C[-4])" 'It will give values "MAX" Column
Range("H5").Select
ActiveCell.FormulaR1C1 = "=MIN(RC[-5]:R[4]C[-5])" 'It will give values "MIN" Column
Range("I5").Select
ActiveCell.FormulaR1C1 = "=AVERAGE(RC[-6]:R[4]C[-6])" 'It will give values "AVG" Column
End If
Next
Did some quick, which should work.
I don't know what you want to do in the "Final" worksheet, so haven't focused on that line.
Logic is to have one big loop (For i...) that go through the whole Column C. When a value is found in column C (If .Cells(i, "C") <> "" Then), we perform a "small loop" (For j = i To lastrow + 1) to check next empty cell to decide the "small group" range. When that range is decided we perform the To, From, MAX, MIN and AVG formulas, which has to be dynamic.
Option Explicit
Sub trial()
Dim lastrow As Long
Dim destSht As Worksheet
Dim i As Long, j As Long
Set destSht = Worksheets("Final")
With Worksheets("Source")
lastrow = .Range("B" & .Rows.Count).End(xlUp).Row
For i = 2 To lastrow + 1 'loop whole range (column C)
If .Cells(i, "C") <> "" Then 'If column C is not empty then
For j = i To lastrow + 1 'Loop "group" range to find next empty cell. Start from current loop i to last row and add one row to get to next empty cell.
If .Cells(j, "C") = "" Then 'When next empty cell is found (i.e. end of small group range) then apply formulas
.Cells(i, "E").Value = .Cells(i, "B").Value 'From
.Cells(i, "F").Value = .Cells(j - 1, "B").Value 'To
.Cells(i, "G").Formula = "=MAX(C" & i & ":C" & j - 1 & ")" 'MAX
.Cells(i, "H").Formula = "=MIN(C" & i & ":C" & j - 1 & ")" 'MIN
.Cells(i, "I").Formula = "=AVERAGE(C" & i & ":C" & j - 1 & ")" 'AVG
Exit For
End If
Next j
End If
Next i
End With
End Sub
Result:

How to fix: If value is true count outcome with other different if statement outcomes?

I got multiple if statements, they check the cells column by column if there is a value "Yes" or "Mediocre". When "Yes" add +1 to the outcome if "Mediocre" add +0.5.
So my problem is as follows: When my code needs to check the next column it needs to do the same but then add +1 or +0.5 to the previous outcome on the same line in the same cell.
This needs to happen on every line.
Eventually when it checked the whole line, the outcome needs to be a percentage: count yes and mediocre then devide by 9 and multiple by 100.
I got an image how it should be below:
Example how it should be
I got the code below:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Application.Intersect(Range("D2:L4"), Range(Target.Address)) Is Nothing Then
'If you add (an)other row(s) edit this code above
Call DeleteP2P4
'If you add (an)other row(s) edit this code above
Call SampleMacro
End If
End Sub
Sub DeleteP2P4()
Range("P2:P4").Select
'If you add (an)other row(s) edit this code above
Selection.ClearContents
End Sub
Sub SampleMacro()
' Get the last row
Dim startRow As Long, lastRow As Long
startRow = 2
lastRow = Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Row
For i = startRow To lastRow
' If there's Yes/Mediocre in D column, then append next number
If Sheet1.Range("D" & i).Value = "Yes" Then
Sheet1.Range("P" & i).Value = "+1"
ElseIf Sheet1.Range("D" & i).Value = "Mediocre" Then
Sheet1.Range("P" & i).Value = "+0.5"
End If
' If there's Yes/Mediocre in E column, then append next number
If Sheet1.Range("E" & i).Value = "Yes" Then
Sheet1.Range("P" & i).Value = "+1"
ElseIf Sheet1.Range("E" & i).Value = "Mediocre" Then
Sheet1.Range("P" & i).Value = "+0.5"
End If
'It continious here with the rest of the If statements
Next
End Sub
As pointed out by Brownish Monster, Change your
Sheet1.Range("P" & i).Value = "+1"
to
Sheet1.Range("P" & i).Value = val(Sheet1.Range("P" & i).Value) + 1
and similarly for Mediocre
Sheet1.Range("P" & i).Value = val(Sheet1.Range("P" & i).Value) + 0.5

Why are there inconsistent and broken cell formulas for some rows and not others?

Goal: Populate F and G columns with proper formulas depending on total PROD-TIME for a block
This is another issue that has come up after one of my previous questions:
How to loop through "blocks" of rows in excel dataset instead of each row individually (VBA)?
I have been able to loop through blocks of rows and can now get the sum of the PROD-TIME for that particular block. This sum is necessary to determine which formula needs to be used in the F and G columns.
This is best illustrated in this workbook,
https://www.dropbox.com/s/vgnqi00h8xosja3/wip%20Gantt%20Template.xlsx?dl=0 , where I have shown how I want the formulas to end up in the F and G columns. But for some reason when I run the macro, it just completely breaks. Some of the formulas don't even use reference cells and use the cell value instead, or reference cells don't even appear. Are the blank F and G columns confusing the macro? How can I make sure that every F and G cell gets filled with something? Errors are fine
Sub getStartEndDate()
Dim WrkSht As Excel.Worksheet
Dim RngColumn As Range, RngBlock As Range
Dim totalHrs As Integer 'total PROD-TIME for the given RngBlock
Dim lastRow As Long
lastRow = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
Set WrkSht = ActiveWorkbook.Worksheets(1)
' Populate the last row by itself first since the With-statement below needs to reference rows below current row
Range("E" & lastRow).Formula = "=ROUNDUP(D" & lastRow & "/12,0)"
Range("G" & lastRow).Value = Range("C" & lastRow).Value
Range("F" & lastRow).Formula = "=WORKDAY(G" & lastRow & ", -E" & lastRow & ")"
Columns("F:F").NumberFormat = "yyyy-mm-dd"
With WrkSht
Set RngColumn = .Range("B2:B" & lastRow)
'Starts the first block with the first cell in a column.
Set RngBlock = RngColumn.Cells(1)
'Checks every cell in a column.
For Each rngcell In RngColumn
If rngcell.Offset(0,1).Value <> "" Then
'Checks whether a cell's value equals the cell below it.
If rngcell.Value = rngcell.Offset(1, 0).Value Then
'If equal, includes the cell below in the block.
Set RngBlock = Union(RngBlock, rngcell.Offset(1, 0))
Else
'If not equal, that means the block RngBlock ends
' totalHrs is the sum of the "PROD-TIME" for that particular block
totalHrs = WorksheetFunction.Sum(Range(CStr(Trim(Chr(64 + RngBlock.Column + 2))) _
& CStr(Trim(Str(RngBlock.Row))) & ":" _
& CStr(Trim(Chr(64 + 2 + RngBlock.Column + RngBlock.Columns.Count - 1))) _
& CStr(Trim(Str(RngBlock.Row + RngBlock.Rows.Count - 1)))))
If totalHrs < 12 Then
' If total production time (PROD-TIME) is less than 12 hours, then the start and end date should be the same for all rows in that block
rngcell.Offset(0, 4).Value = rngcell.Offset(0, 1).Value
rngcell.Offset(0, 5).Value = rngcell.Offset(0, 1).Value
Else
' If total production time is greater than 12 hours, then start and end dates are based on a different formula
' e.g. Given row 11, F column formula looks like: =WORKDAY(G11, -E11), G column looks like: =IF(B11=B12,F12,C11)
rngcell.Offset(0, 4).Formula = "=WORKDAY(" & rngcell.Offset(0, 5) & ", -" & rngcell.Offset(0, 3) & ")"
rngcell.Offset(0, 5).Formula = "=IF(" & rngcell & "=" & rngcell.Offset(1, 0) & "," & rngcell.Offset(1, 4) & "," & rngcell.Offset(0, 1) & ")"
End If
'Starts the next block with the cell below.
Set RngBlock = rngcell.Offset(1, 0)
End If
End If
Next rngcell
End With
End Sub

Central worksheet that distributes content to other worksheets based on certain criteria

Is there a macro that can transfer rows of data to a different worksheet depending on a certain value from that row. For example, I have the following data in a central worksheet, Masterlog.xls:
(a hyphen denotes a column)
Apples - 12312312
Green Apples - 32132132
Mangoes - 00000000
Green Mangoes - 11111111
Bananas - 22222222
The masterlog data is updated daily and manually by 2 people. Sometimes the data is a duplicate of yesterday's and I was also hoping for a macro that ignores duplicates and remembers data from 2 days ago but clears data that is older.
Column A's value will determine which worksheet the row of data will go to.
Apples - 12312312 should be transferred to the Worksheet Apples.xls
Green Apples - 32132132 is also transferred to Apples.xls on the next row.
Mangoes - 00000000 goes to Mangoes.xls and so on.
The macro should always write on the first empty row after the last one with content.
The Apples.xls, Mangoes.xls and Bananas.xls are shared worksheets with 22 users.
This code will do the trick of copying the data to the appropriate sheet. It doesn't clear the sheets beforehand, meaning that if you run the macro multiple times, it will add the same entries after the entries already there, but it should give you a palce to start.
Option Explicit
Sub test()
Dim col As New Collection, cell As Range, ChkRng As Range, entry As Variant, lstRw As Long, i As Long
With Sheets("Masterlog")
Set ChkRng = .Range("A4:A" & .Range("A" & .Rows.Count).End(xlUp).Row)
End With
On Error Resume Next
For Each cell In ChkRng
If col.Count = 0 Then GoTo Add
For i = 1 To col.Count
If cell.Value Like "*" & col.Item(i) Then
GoTo continue
End If
Next i
Add:
col.Add cell.Value, cell.Value
continue:
Next cell
On Error GoTo 0
For Each cell In ChkRng
For i = 1 To col.Count
If cell.Value = col.Item(i) Then
If WorksheetExists(col.Item(i)) = False Then
Worksheets.Add , Sheets("Masterlog")
ActiveSheet.Name = col.Item(i)
With Sheets(col.Item(i))
lstRw = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("A" & lstRw) = cell.Value
.Range("A" & lstRw).Offset(0, 1) = cell.Offset(0, 1).Value
End With
Else
With Sheets(col.Item(i))
lstRw = .Range("A" & .Rows.Count).End(xlUp).Row + 1
.Range("A" & lstRw) = cell.Value
.Range("A" & lstRw).Offset(0, 1) = cell.Offset(0, 1).Value
End With
End If
GoTo onwards
ElseIf cell.Value Like "*" & col.Item(i) = True Then
If WorksheetExists(col.Item(i)) = False Then
Worksheets.Add , Sheets("Masterlog")
ActiveSheet.Name = col.Item(i)
With Sheets(col.Item(i))
lstRw = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("A" & lstRw) = cell.Value
.Range("A" & lstRw).Offset(0, 1) = cell.Offset(0, 1).Value
End With
Else
With Sheets(col.Item(i))
lstRw = .Range("A" & .Rows.Count).End(xlUp).Row + 1
.Range("A" & lstRw) = cell.Value
.Range("A" & lstRw).Offset(0, 1) = cell.Offset(0, 1).Value
End With
End If
GoTo onwards
End If
Next i
onwards:
Next cell
End Sub
Public Function WorksheetExists(ByVal WorksheetName As String) As Boolean
On Error Resume Next
WorksheetExists = (Sheets(WorksheetName).Name <> "")
On Error GoTo 0
End Function
Currently it transfers both the value in Column A and the value in Column B, but you should change that to your needs.
It works for your example. I don't know about more complicated patterns.

Resources