Excel VBA Writing Multiple Scenario Using If Conditional Statement - excel

wk2LastRow = wk2.Cells(Rows.Count, 1).End(xlUp).Row
i = 2
For wk2Range = 2 To wk2LastRow
id = wk2.Cells(wk2Range, 1)
Set f = wk1.Range("A2:I7").Find(id, , xlValues, xlPart)
'Conditional Statement code to be inserted here
Next wk2Range
Background information:
Search values are in Worksheet 2 (wk2).
wk2Range selects the entire search value
"A2:I7" are the values below from 1 to 6
I want to match a list of values to the "A2:I7" (1 to 6). The data that I need from the table is the second OFF day's Date. For example, For name 2, the second off day dates are 01/02/15, 01/06/15.
I want the code to find a match, afterwards read the code horizontally to find the second off day, and then end(xlup) to get the date.
I have thought of the 4 possible scenarios but am unsure of how to write them down into code. I just need the second off days date, the code can ignore the rest of the variations.
On On Off Off
On Off Off On
Off Off On On
Off On On Off
Please see picture for elaboration:

This is not the most elegant solution but should return the dates you are hoping for:
Private Sub ViewHolidayDates()
'Defined rota range:
Set Rota = Sheet1.Range("B2:I7").Cells
'How many dates across.
Dim RowLength As Integer: RowLength = 8
'Which Name we are looping.
Dim Row As Integer: Row = 1
'Where within the range loop we are.
Dim UserRecord As Integer: UserRecord = 0
'Current count of "off" for current Row.
Dim Holiday As Integer: Holiday = 0
Dim DateValue As String
For Each Record In Rota
If Record = "off" Then
Holiday = Holiday + 1
If Holiday = 2 Then
DateValue = Sheet1.Range("B2").Offset(-1, UserRecord).Value
MsgBox (DateValue)
'reset Holiday counter:
Holiday = 0
End If
End If
UserRecord = UserRecord + 1
If UserRecord = RowLength Then
'Reset counters ready for new "Name"
Row = Row + 1
UserRecord = 0
Holiday = 0
End If
Next Record
End Sub
Please set the Rota variable in my code to the range in your Excel Sheet and also the Sheet1.Range("B2") in my code to be the first record in your range.
UPDATE:
You can adjust the RowLength and the If Holiday = 2 to values more suited to your scenario. You should also note "off" is case sensitive.
UPDATE
I have updated the Holiday counter to reset within the IF statement, this will then return a date for each instance of the second "off".
Also, my Row - Row - 1 was not required, as the range I defined was fixed: Sheet1.Range("B2") we need only to say Offset(-1, UserRecord) and dynamically pass in how many columns across we expect the date to be.

Here's one way to do it, just assign SecondOff to the range of your choice in the calling sub. Note that there are probably better ways to do it than with Find, but I wanted to stay in sync with your question.
Public Function SecondOff(Schedule As Range) As Variant
Dim rw As Range, Temp As Variant
Dim i As Integer, j As Integer
Dim wrow As Integer, firstI As Integer, origI As Integer
ReDim Temp(1 To 6, 1 To 9)
For Each rw In Schedule.Range("2:" & Schedule.Rows.Count).Rows
i = 1
j = 2
wrow = rw.Cells(1, 1)
Temp(wrow, 1) = wrow
If (rw.Find("off", rw.Cells(1, i), xlValues, xlPart) Is Nothing) Then
Temp(wrow, 2) = "None"
Else
origI = rw.Find("off", rw.Cells(1, i), xlValues, xlPart).Column
Do
firstI = rw.Find("off", rw.Cells(1, i), xlValues, xlPart).Column
If firstI <= origI And j > 2 Then Exit Do
i = rw.Find("off", rw.Cells(1, firstI), xlValues, xlPart).Column
If i <= origI Then Exit Do
If i - firstI = 1 Then
Temp(wrow, j) = Schedule(1, i)
j = j + 1
Else
i = firstI
End If
Loop Until j > 8
End If
Next rw
SecondOff = Temp
End Function

Related

Split array and compare values to separate columns

Im trying to create a function which scans a column (job-trav-seq) and splits the values in each cell within a given range. It then compares these values to comparable cells in separate columns (so for instance job-trav-seq would have a cell 58546-05-10; this function would remove the dashes and compare the 58546 to job number, 05 to traveller ID and 07 to sequence No.
Basically, the function needs to first takes the A column (JobTravSeq) and breaks it apart into individual variables. (variable 1 should be compared with values in column B, values in variable 2 should be compared with column C and values in variable 3 should be compared with column D)
A loop should go up through the column cells as long as variable 1 = values in column B and variable 2 = values in column C (this is rowStart); this should be stored as a variable
A second loop should occur (rowEnd); which should loop down though the column cells as long as variable 1 = values in column B and variable 2 = values in column C; this should be stored as a variable
The code should then traverse between rowStart and rowEnd and check if variable 3 = values in column D, if it does then place an asterisk (or something similar) in front of the value to mark it as a current task
What im starting with: Sample Doc
What im trying to achieve: SampleDocOutput
any help would be most appreciated
heres my code for reference:
Sub SampleDocOrganise()
Dim i As Integer
Dim LastRow, rowCompare As Long
Dim variArr, rowStart, rowEnd, rangeID As Variant
Dim JobTravSeqRng As Range, jobNoRng As Range, TravellerRng As Range,
opSeqRng As Range, _
rng_JobTravSeq As Range, rng_JobNo As Range, rng_Traveller As Range,
rng_opSeq As Range
Set JobTravSeqRng = Range("A:A")
Set jobNoRng = Range("B:B")
Set TravellerRng = Range("C:C")
Set opSeqRng = Range("D:D")
For Each JobTravSeq In Selection
Str_Array = Split(JobTravSeq, "-")
For h = 0 To UBound(Str_Array)
Range("A:A").Find (Str_Array)
Range.Offset(, h + 1) = Str_Array(h)
For rowStart = 4 To Rows.Count
If Worksheets("Sheet1").Cells(Str_Array, 1).Value = jobNoRng.Value Then
If Cells(Str_Array, 2).Value = jobNoRng.Value Then
Cells.Value = rowStart
End If
End If
Next rowStart
For rowEnd = LastRow To 4 Step -1
If Cells(Str_Array, 1).Value = Range("B:B").Value Then
If Cells(Str_Array, 2).Value = Range("C:C").Value Then
Cells.Value = rowEnd
End If
End If
Next rowEnd
For rowCompare = rowStart To rowEnd
For Each opSeqArr In Str_Array
If Cells(Str_Array, 3).Value = Range("D:D").Value Then
If Cells(Str_Array, 1).Value = Range("B:B") Then
ActiveCell.Characters(0, 0).Insert (" P ")
With ActiveCell.Characters(0, Len(" P ")).Font
.Name = "OpSeq_Equals"
.Bold = True
.Color = -16776961
End With
MsgBox cell.Value = "*" & ""
' if cell changes then go to next loop
Else
' if cell changes then go to next loop
End If
End If
Next
Next
Next h
Next
End Sub
Sub MsgboxTasks() 'should display all rows that contain an asterisk in opSeq (current tasks)
End Sub

How to count a pair of texts in one column that are few rows apart using vba?

In workbook A, I'm trying to count when a text, "Dr" occurs then within 5 rows after it, how many cells are blank or the cell is either a text, "Nr" or "Cr".
In another word, I'm trying to count the numbers of pairs of "DR-blank(within 5 rows after DR)", "DR-NF(within 5 rows after DR)", and "DR-CR(within 5 rows after DR)". The data set looks like this:
Column A 0 1 2 3 4 5 6 7 8
Column B Dr Cr Dr Nr
And then I want to copy the result to workbook B.
I've been tried to use offset:
If Range("B2:B901").Value = "D" Then
'V3 = Application.WorksheetFunction.CountBlank(.Range("B2:B901").Offset(5, 0))
Wb.Worksheets("Sheet1").Cells(Rows.Count, "M").End(xlUp).Offset(1, 0).Value = V3
But I always got a "0" in return, meaning the logic wasn't quite right to capture what I intended to do.
Could someone help with the codes? Really appreciated!
This code will iterate through every cell in the range you provide (in this case B1:B901 in sheet1) and if it contains the vale Dr it will then iterate through the subsequent 5 cells to check if they contain the values you are looking for.
It will output the contents of column A and column B to a new workbook, together with your count of nr, cr and blank in columns c, d and e respectively.
Option Compare Text 'this tells VBA that you want you string comparisons to NOT be
'case sesitive. If you want case to be taken into account, then leave
'this line out.
Sub test()
Dim cll As Range
Dim vCellValue As Variant
Dim iterator As Integer
Dim vCountBlank As Integer
Dim vCountCr As Integer
Dim vCountNr As Integer
Dim wb2 As Workbook
Set wb2 = Workbooks.Add
For Each cll In Sheet1.Range("B2:B901")
vCountBlank = 0
vCountCr = 0
vCountNr = 0
If cll.Value = "Dr" Then
For iterator = 1 To 5
vCellValue = cll.Offset(iterator, 0).Value
If vCellValue = "Nr" Then vCountNr = vCountNr + 1
If vCellValue = "Cr" Then vCountCr = vCountCr + 1
If vCellValue = "" Then vCountBlank = vCountBlank + 1
Next iterator
End If
wb2.Sheets(1).Cells(cll.Row, 1).Value = cll.Offset(0, -1).Value
wb2.Sheets(1).Cells(cll.Row, 2).Value = cll.Value
wb2.Sheets(1).Cells(cll.Row, 3).Value = vCountNr
wb2.Sheets(1).Cells(cll.Row, 4).Value = vCountCr
wb2.Sheets(1).Cells(cll.Row, 5).Value = vCountBlank
Next cll
Set wb2 = Nothing
End Sub

Coloring Excel rows

So i found this script on this site to color rows with the same cell-data and change the color when the celldata changes and it seems to work just fine, but i have two minor issues
It seems to only apply to the first 900 rows (I have an excel list with 8000+ rows)
It colors the entire row, is there a way to make it only color a certain part of the row?
Thanks in advance! here's the script:
Public Sub HighLightRows()
Dim i As Integer
i = 2 'start at 2, cause there's nothing to compare the first row with
Dim c As Integer
c = 2 'Color 1. Check http://dmcritchie.mvps.org/excel/colors.htm for color indexes
Do While (Cells(i, 1) <> "")
If (Cells(i, 1) <> Cells(i - 1, 1)) Then 'check for different value in cell A (index=1)
If c = 2 Then
c = 37 'color 2
Else
c = 2 'color 1
End If
End If
Rows(Trim(Str(i)) + ":" + Trim(Str(i))).Interior.ColorIndex = c
i = i + 1
Loop
End Sub
Try this:
Public Sub HighLightRows()
Const START_ROW As Long = 2 '<< use a Constant for fixed values
Const VALUE_COL As Long = 1
Dim rw As Range, emptyCells As Long, i As Long, currentValue, tmp
Dim arrColors
arrColors = Array(37, 2)
Set rw = ActiveSheet.Rows(START_ROW)
currentValue = Chr(0) 'dummy "current value"
Do While emptyCells < 10 'quit after 10 consecutive empty cells
tmp = rw.Cells(VALUE_COL).Value
If Len(tmp) > 0 Then
If tmp <> currentValue Then
i = i + 1
currentValue = tmp 'save the new value
End If
'assign the color to a specific set of cells in the row
' starting at cell 1 and 5 columns wide
rw.Cells(1).Resize(1, 5).Interior.ColorIndex = arrColors(i Mod 2)
emptyCells = 0 'reset empty row counter
Else
emptyCells = emptyCells + 1 'increment empty row counter
End If
Set rw = rw.Offset(1, 0) 'next row
Loop
End Sub
It looks like the code only evaluates if the cell is the same as the cell above it. Conditional formatting, as John Coleman said, would be more effective. With it values in the whole column can be evaluated instead of just adjacent ones. And, if I'm not mistaken, there's a setting to look for dup values since Excel 2007, so there doesn't have to be some kind of formula kung-fu to do it.
Unless I'm missing something, it's as simple as Conditional Formatting -> Highlight Cell Rules -> Duplicate Values.

Hide rows according to range of cells

Good day, I would love to ask you a question.
I have two colls with numbers and I need to compare first coll (longer) with second coll (shorter) and if there is a match, hide the row where the match occurs.
I have this so far:
Sub RowHide()
Dim cell As Range
Dim CompareCells As Range
Set CompareCells = Range("I2:I18")
For Each cell In Range("A2:A200")
If cell.Value = CompareCells Then
cell.EntireRow.Hidden = True
End If
Next
End Sub
My problem is that I don't know how to set value of CompareCells to start comparing. I'll appreciate every advice.
You have to set 2 separate ranges and compare them. If you want every cell compared with the one on the same line (A1 with B1, A2 with B2, etc) then consider using:
for i = 1 to something
set cell1 = range("A" & i)
set cell2 = range("B" & i)
if cell1.value = cell2.value then
'Do this, and do that!
cell1.entirerow.hidden = true
end if
next i
try this:
Sub RowHide()
Dim Longer As Range
Dim i As Double
i = 2 'Initial row
For Each Longer In Range("A2:A200")
If Longer.Value = Cells(i,2).Value Then
Longer.EntireRow.Hidden = True
End If
i = i + 1
Next
End Sub
PS:
Cells(RowIndex, ColumnIndex).Value: returns the value of the Row And Column.
ColumnIndex => Column A = 1, Column B = 2, an so on...
I looked into both of yours ideas and converted them into one and I finally get it working.
Here is my final code:
Sub RowHide()
Dim i As Integer
Dim j As Integer
For i = 2 To 197
Set FirstRange = Range("A" & i)
For j = 2 To 18
If FirstRange.Value = Cells(j, 8).Value Then
FirstRange.EntireRow.Hidden = True
End If
Next j
Next i
End Sub
Only modification if someone wants to use it is that you have to change numbers in for cycles according to number of rows in columns.
Thanks to both of you for your advices.

VBA Looping/Logic Issue

I am writing a macro in excel for work and I am having trouble. In this scenario there are two sheets, "BU" and "TOPS Information". When the macro is used it is supposed to search every line of "BU" for the value found in "TOPS Information", then go to the next line of "TOPS Information and repeat the process. If it finds a correct match it is supposed to copy a cell and paste it into "TOPS Information".
Here is the code:
Sub QIM()
Dim j As Integer
Dim k As Integer
Dim i As Integer
Dim l As Integer
Dim m As Integer
Dim searchArray(1 To 3) As String
j = 0
k = 1
'WARNING: Temporary Sheet Names
lastRowTOPS = Worksheets("TOPS Information").Cells(Rows.Count, "A").End(xlUp).Row
lastRowBU = Worksheets("BU").Cells(Rows.Count, "A").End(xlUp).Row
'Cycle through BU rows
For j = lastRowTOPS To 1 Step -1
'Cycle through searchArray for each BU row
For k = lastRowBU To 1 Step -1
'//////////////////////////////////////
x = Sheets("BU").Range("B" & k).Value
y = Range("C" & j).Value
If StrComp(x, y) = 1 Then
Sheets("BU").Range("C" & k).Copy
Range("H" & j).PasteSpecial
End If
'//////////////////////////////////////
Next k
Next j
End Sub
This Macro obviously only works if "TOPS Information" is selected at the time. Any and all help would be most appreciated. THANKS!
You sorta answered it yourself. Range refers to the current sheet, but when you're bouncing around then you have to qualify it.
Prefix your ranges with the appropriate sheet like so,
Sub QIM()
Dim j As Integer
Dim k As Integer
Dim i As Integer
Dim l As Integer
Dim m As Integer
Dim searchArray(1 To 3) As String
j = 0
k = 1
'WARNING: Temporary Sheet Names
lastRowTOPS = Worksheets("TOPS Information").Cells(Rows.Count, "A").End(xlUp).Row
lastRowBU = Worksheets("BU").Cells(Rows.Count, "A").End(xlUp).Row
'Cycle through BU rows
For j = lastRowTOPS To 1 Step -1
'Cycle through searchArray for each BU row
For k = lastRowBU To 1 Step -1
'//////////////////////////////////////
x = Sheets("BU").Range("B" & k).Value
y = Sheets("TOPS Information").Range("C" & j).Value
If StrComp(x, y) = 1 Then
Sheets("BU").Range("C" & k).Copy
Sheets("TOPS Information").Range("H" & j).PasteSpecial
End If
'//////////////////////////////////////
Next k
Next j
End Sub
Assuming only want to copy the top most found data in BU to TOPS, you can use below.
Sub QIM()
Dim oWS_TOPS As Worksheet, oWS_BU As Worksheet ' Worksheet objects
Dim oRng_TOPS As Range, oRng_BU As Range ' Range objects
Dim R_TOPS As Long, R_BU As Long
Set oWS_TOPS = ThisWorkbook.Worksheets("TOPS Information") ' <-- Replace this "TOPS Information" to match future changes
Set oWS_BU = ThisWorkbook.Worksheets("BU") ' <-- Replace this "BU" to match future changes
R_TOPS = oWS_TOPS.Cells(Rows.Count, "A").End(xlUp).Row
R_BU = oWS_BU.Cells(Rows.Count, "A").End(xlUp).Row
' Search column B of BU for each cell in column C of TOPS
For Each oRng_TOPS In oWS_TOPS.Columns("C").Cells ' <-- Replace this "C" to match future changes
' Exit if row is more than last A column data
If oRng_TOPS.Row > R_TOPS Then Exit For
For Each oRng_BU In oWS_BU.Columns("B").Cells ' <-- Replace this "B" to match future changes
' Exit if row is more than last A column data
If oRng_BU.Row > R_BU Then Exit For
' Check if Ranges match (## See Update ##)
If InStr(1, oRng_TOPS.Value, oRng_BU.Value, vbTextCompare) > 0 Then
' Copy column C of found row in BU to column H of TOPS, then exit
oWS_BU.Cells(oRng_BU.Row, "C").Copy oWS_TOPS.Cells(oRng_TOPS.Row, "H") ' <-- Replace these "C" and "H" to match future changes
Exit For
End If
Next
Next
Set oWS_TOPS = Nothing
Set oWS_BU = Nothing
End Sub
There are many ways to achieve your goal, and this is one of it.
UPDATE Note on comparing cell values (String):
StrComp(S1,S2[,mode]) only return 3 values {-1, 0, 1} to indicate if S1 is less/equal/greater than S2. If you want an exact match (case sensitive and exact spacing), use If StrComp(S1,S2) = 0 Then.
InStr([i,]S1,S2[,mode]) only returns positive values - it returns the character location of first appearance of S2 in S1. If S2 is not found then it returns zero.
You can also use Trim(sText) to remove leading/ending spaces of sText.
Hope below screenshot says more.

Resources