Append string to cells depending on values in other cells - vba

I am working on a project to scans a list of dates in one column and values in another, and then appends a value to a string in a third column. What I am having trouble with is appending that same value to every cell above the specified date. The data looks like this:
What I need to have happen is for the values of T1, T2 etc. to be appended to the code for every cell above the last_month_row which does not already have a value appended. It should look like this:
My code thus far is this:
Sub Test_Logic()
Dim lastrow As Long, lastcolumn As Long, lastrow_reps As Long
Dim tmp As String, arr() As String, msg As String
Dim cell As Range
Dim i As Integer, j As Integer
Dim last_month As Long
Dim last_month_row As String, first_month_row As String
Dim ws As Worksheet, ws2 As Worksheet
Dim wb As Workbook
Dim reps As Variant, quota As Variant, repslist As Variant, ACV As Variant
Set wb = ActiveWorkbook
Set ws2 = wb.Sheets("Rep_Commission")
lastrow_reps = ws2.Cells(Rows.Count, 1).End(xlUp).Row
Set repslist = ws2.Range("A3:A" & (lastrow_reps))
Set ACV = ws2.Range("B3:B" & (lastrow_reps))
With wb
For Each reps In repslist
Set ws = Worksheets(reps.Text)
Set ACV = ws2.Range("A1:A99").Find(reps, LookIn:=xlValues).Offset(, 1)
lastrow = ws.Cells(Rows.Count, 1).End(xlUp).Row
For Each cell In ws.Range("I2:I" & lastrow)
If (cell <> "") And (InStr(tmp, cell) = 0) Then
tmp = tmp & cell & "|"
End If
Next cell
If Len(tmp) > 0 Then tmp = Left(tmp, Len(tmp) - 1)
arr = Split(tmp, "|")
For i = LBound(arr) To UBound(arr)
msg = msg & arr(i) & vbNewLine
Next i
For i = LBound(arr) To UBound(arr)
'the error occurs here
last_month = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 6).Value
last_month_row = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 7).Row
first_month_row = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlNext).Offset(, 7).Address
If last_month < (ACV / 2) Then
ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 7).Value = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 5).Value & "T1"
For j = 2 To last_month_row - 1
If ws.Range("I" & j).Value = arr(i) Then
ws.Range("P" & j).Value = ws.Range("P" & j).Value & "T1"
End If
Next j
ElseIf last_month > (ACV / 2) And last_month < ACV Then
ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 7).Value = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 5).Value & "T2"
For j = 2 To last_month_row - 1
If ws.Range("I" & j).Value = arr(i) Then
ws.Range("P" & j).Value = ws.Range("P" & j).Value & "T2"
End If
Next j
ElseIf last_month > ACV And last_month < (ACV * 1.5) Then
ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 7).Value = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 5).Value & "T3"
For j = 2 To last_month_row - 1
If ws.Range("I" & j).Value = arr(i) Then
ws.Range("P" & j).Value = ws.Range("P" & j).Value & "T3"
End If
Next j
ElseIf last_month > (ACV * 1.5) Then
ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 7).Value = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 5).Value & "T4"
For j = 2 To last_month_row - 1
If ws.Range("I" & j).Value = arr(i) Then
ws.Range("P" & j).Value = ws.Range("P" & j).Value & "T4"
End If
Next j
Else 'Do nothing yet, or maybe some error handling??
End If
Next i
Next reps
End With
End Sub
What I can't figure out is how to add the "T" values to the correct cells - I tried looping through a range created by first_month_row and last_month_row but ended up just appending multiples of the T values. Any advice would be appreciated.
EDIT: So I was successfully able to populate the cells, but when I try to loop through my worksheets I get an "Object Variable or With block Variable Not Set" Error. It occurs at this line:
last_month = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 6).Value
But last_month is a single value, not an object so I don't see why this is occurring.

Your sub does exactly what you have programmed. It finds the last occurence of arr(i) and sets the value in column P for the last row only. You'd rather loop this way to go through the ranges with the same date:
Dim last_month_row as long, first_month_row as long, k as long
For i = LBound(arr) To UBound(arr)
last_month_row = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlPrevious).Offset(, 7).row
first_month_row = ws.Range("I2:I" & lastrow).Find(What:=arr(i), searchdirection:=xlNext).Offset(, 7).row
For k = first_month_row to last_month_row
If last_month < (ACV / 2) Then
ws.cells(k, "P").Value = ws.cells(k, "N").Value & "T1"
...
Additionally, I suggest using Long instead of Integer. VBA works with longs, you do not save anything with integers, but you risk an overflow going above 65K.

I think you need to add another Array/Collection that contains "ONLY" the "Unique" values for column "I", then use this Unique values to iterate/search inside your selection as the following pseudo code:
For Each Unq in UniqueDate
For i = LBound(arr) To UBound(arr)
// Here, use "Unq" instead of "arr(i)" to search/find your target value
next i
next Unq
For me, the easiest way to create a unique 'list' is by using "Collection" :
Dim UniqDate As New Collection
ws.Range("I2:I" & lastrow).Select
If Not Selection Is Nothing Then
For Each cell In Selection
UniqDate.Add cell, cell // Collection will ignore the value of "cell" if already exist (duplicated values filtering)
// the rest of your code is the same
If (cell <> "") And (InStr(tmp, cell) = 0) Then
tmp = tmp & cell & "|"
End If
Next cell
End If

Related

Change first 2 charecters in cell to integer (with a loop (VBA))

I was trying to create a script in vba which checks the cell "A1" and replaces the first 2 characters of the string with an integer. Should look something like this:
If (Left(A1, 2) = "NI") Then
newtext = Replace("originaltext", "NI", "801")
ElseIf (Left(A1, 2) = "RE") Then
newtext = Replace("originaltext", "RE", "821")
ElseIf (Left(A1, 2) = "NV") Then
newtext = Replace("originaltext", "NV", "571")
ElseIf (Left(A1, 2) = "NF") Then
newtext = Replace("originaltext", "NF", "831")
end if
n=n+1
Loop
I want it to go through every cell and do that action starting from a1 lets say all the way down to a2 and a3 and so on.
How do I create the right loop for that?
Consider:
Sub flay()
Dim i As Long, N As Long, v As Variant, frst As String
N = Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To N
v = Cells(i, "A").Value
If Len(v) >= 2 Then
frst = Left(v, 2)
If frst = "RE" Then
Cells(i, "A").Value = Replace(v, "RE", "821")
ElseIf frst = "NI" Then
Cells(i, "A").Value = Replace(v, "NI", "801")
ElseIf frst = "NF" Then
Cells(i, "A").Value = Replace(v, "NF", "831")
ElseIf frst = "NV" Then
Cells(i, "A").Value = Replace(v, "NV", "571")
End If
End If
Next i
End Sub
Try the next code, please:
Sub testReplaceWithNumber()
Dim sh As Worksheet, lastR As Long, i As Long
Set sh = ActiveSheet 'use here the necessary sheet
lastR = sh.Range("A" & sh.Rows.count).End(xlUp).row
For i = 1 To lastR
Select Case left(sh.Range("A" & i).Value, 2)
Case "NI":
sh.Range("A" & i).Value = "801" & Mid(sh.Range("A" & i).Value, 3)
Case "RE":
sh.Range("A" & i).Value = "821" & Mid(sh.Range("A" & i).Value, 3)
Case "NV":
sh.Range("A" & i).Value = "571" & Mid(sh.Range("A" & i).Value, 3)
Case "NF":
sh.Range("A" & i).Value = "831" & Mid(sh.Range("A" & i).Value, 3)
End Select
Next i
End Sub
For each cl in
application.intersect(columns(1),activesheet.usedrange)
Select case left(cl,2)
Case "RE"
Mid(cl,1,2) ="821"
Case "NI"
Mid(cl,1,2) ="801"
' And so on
End select
Next

For Loop VBA Speed up by putting into an array

I am trying to go through about 2000 lines worth of data and do a for loop and several if statements within each loop. It works, but right now it is painfully slow. I understand from my research that if I can put the data into an array and manipulate it there and then put the data back into the cells would be much faster but I could use some help on the coding to do so. Here is my code.
Sub EliminateVariance()
Dim Old As Long
Dim Older As Long
Dim Oldest As Long
Dim Current As Long
Dim VarianceOld As Long
Dim VarianceNew As Long
Dim VarianceNew1 As Long
Dim VarianceNew2 As Long
Dim Month1 As Variant
Dim SheetName As Variant
Dim LastRow As Long
Dim i As Long
Month1 = InputBox("What is this month?")
SheetName = Month1 & " SummaryByCust"
Worksheets(SheetName).Activate
LastRow = Cells(Rows.count, "B").End(xlUp).row
For i = 3 To LastRow
VarianceOld = Range("V" & i)
Oldest = Range("I" & i)
Older = Range("H" & i)
Old = Range("G" & i)
Current = Range("F" & i)
If VarianceOld > Oldest Then
VarianceNew = VarianceOld - Oldest
Range("I" & i) = 0
If VarianceNew > Older Then
VarianceNew1 = VarianceNew - Older
Range("H" & i) = 0
If VarianceNew1 > Old Then
VarianceNew2 = VarianceNew1 - Old
Range("G" & i) = 0
If VarianceNew2 > Current Then
MsgBox ("Error: Deferred is greater than what it should be. Verify your numbers")
Else
Range("F" & i) = Current - VarianceNew2
End If
Else
Range("G" & i) = Old - VarianceNew1
End If
Else
Range("H" & i) = Older - VarianceNew
End If
Else
Range("I" & i) = Oldest - VarianceOld
End If
Next i
End Sub
Here is an example on how to use Arrays:
Sub arrayEx()
'Set the range
Dim rng As Range
Set rng = Worksheets("Sheet1").Range("A1:B20000")
'Bulk load the values from the range into an array
'Even if a single column this will create a 2D array
Dim rngArr As Variant
rngArr = rng.Value
'Loop the "Rows" of the array
Dim i As Long
For i = LBound(rngArr, 1) To UBound(rngArr, 1)
'Do something with that array
'when loaded from a range it is similar nomenclature to Cells: array(row,column)
If rngArr(i, 1) = "A" Then
rngArr(i, 2) = "B"
End If
Next i
'overwrite the values in range with the new values from the array.
rng.Value = rngArr
End Sub
Try to adapt to fit your needs.

Combining consecutive values in a column with the help of VBA

I have a data like this :
A049
A050
A051
A053
A054
A055
A056
A062
A064
A065
A066
And I want the output like :
As you can see, I want the ranges which are in consecutive order
I am trying some thing like this:
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
For i = 2 To lastRow
r = wb.Sheets("Sheet1").Range("A" & i).Value
If wb.Sheets("Sheet1").Range("A" & i).Value = wb.Sheets("Sheet1").Range("A" & i+1).Value
Next i
End Sub
But not helping me
Am feeling charitable so have tried some code which should work. It assumes your starting values are in A1 down and puts results in C1 down.
Sub x()
Dim v1, v2(), i As Long, j As Long
v1 = Range("A1", Range("A" & Rows.Count).End(xlUp)).Value
ReDim v2(1 To UBound(v1, 1), 1 To 2)
For i = LBound(v1, 1) To UBound(v1, 1)
j = j + 1
v2(j, 1) = v1(i, 1)
If i <> UBound(v1, 1) Then
Do While Val(Right(v1(i + 1, 1), 3)) = Val(Right(v1(i, 1), 3)) + 1
i = i + 1
If i = UBound(v1, 1) Then
v2(j, 2) = v1(i, 1)
Exit Do
End If
Loop
End If
If v1(i, 1) <> v2(j, 1) Then v2(j, 2) = v1(i, 1)
Next i
Range("C1").Resize(j, 2) = v2
End Sub
Try the below code
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
Dim lastNum, Binsert As Integer
Dim firstCell, lastCell, currentCell As String
Binsert = 1
lastNum = getNum(wb.Sheets("Sheet1").Range("A1").Value)
firstCell = wb.Sheets("Sheet1").Range("A1").Value
For i = 2 To lastRow
activeNum = getNum(wb.Sheets("Sheet1").Range("A" & i).Value)
currentCell = wb.Sheets("Sheet1").Range("A" & i).Value
If (activeNum - lastNum) = 1 Then
'nothing
Else
lastCell = wb.Sheets("Sheet1").Range("A" & (i - 1)).Value
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> lastCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = lastCell
End If
Binsert = Binsert + 1
firstCell = wb.Sheets("Sheet1").Range("A" & i).Value
End If
lastNum = activeNum
Next i
'last entry
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> currentCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = currentCell
End If
End Sub
Public Function getNum(ByVal num As String) As Integer
getNum = Val(Mid(num, 2))
End Function
Another solution. It loops backwards from last row to first row.
Option Explicit
Public Sub FindConsecutiveValues()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim lRow As Long 'find last row
lRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim lVal As String 'remember last value (stop value)
lVal = ws.Range("A" & lRow).Value
Const fRow As Long = 2 'define first data row
Dim i As Long
For i = lRow To fRow Step -1 'loop from last row to first row backwards
Dim iVal As Long
iVal = Val(Right(ws.Range("A" & i).Value, Len(ws.Range("A" & i).Value) - 1)) 'get value of row i without A so we can calculate
Dim bVal As Long
bVal = 0 'reset value
If i <> fRow Then 'if we are on the first row there is no value before
bVal = Val(Right(ws.Range("A" & i - 1).Value, Len(ws.Range("A" & i - 1).Value) - 1)) 'get value of row i-1 without A
End If
If iVal - 1 = bVal Then
ws.Rows(i).Delete 'delete current row
Else
If lVal <> ws.Range("A" & i).Value Then 'if start and stop value are not the same …
ws.Range("B" & i).Value = lVal 'write stop value in column B
End If
lVal = ws.Range("A" & i - 1).Value 'remember now stop value
End If
Next i
End Sub

Find out the cases having discrepancies

I am having a sheet having seven cloumns. First six columns having either true or false and in last column I have to mention the heading of false cases in one statement. Below is the excel.
Excel sheet
I have tried if else statement but there are too many possibilities. Since I am new to VBA i don't know any shortcut to that.Any suggestions?.... Thanks
Try this simple vba code,
Sub TEXTJOIN()
Dim i As Long, str As String, k As Long, j As Long
str = ""
j = 0
For i = 2 To Cells(Rows.Count, 1).End(xlUp).Row
If Application.WorksheetFunction.CountIf(Range("A" & i & ":F" & i), True) = 6 Then
Cells(i, 7) = "No Discrepancy Found"
Else
For k = 1 To 6
If Cells(i, k) = False Then
str = str & Cells(1, k) & ","
j = j + 1
End If
Next k
str = Left(str, Len(str) - 1) & " mismatch found"
Cells(i, 7) = Application.WorksheetFunction.Substitute(str, ",", " and ", j - 1)
str = ""
j = 0
End If
Next i
End Sub
Here's simple code which you should try:
Sub FindDiscrepancies()
Dim lastRow, i, j As Long
Dim discrepancies As String: discrepancies = ""
'find number of last row
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lastRow
For j = 1 To 6
If LCase(Cells(i, j).Value) = "false" Then
discrepancies = discrepancies & Cells(1, j).Value & ", "
End If
Next j
If discrepancies = "" Then
Cells(i, 7).Value = "No discrepancies found"
Else
Cells(i, 7).Value = "Mismatch found in " & discrepancies
End If
discrepancies = ""
Next i
End Sub

VBA Copy paste columns in different sheet

I have two sheets – Latency, TP. I need to copy col M from "Latency" and paste it into col D of "TP" only if "Latency" col E has the string “COMPATIBLE” and col O has the string “Pass”.
I have the below code, but it doesn't give any result.
I'm not sure whats wrong with it:
Sub sbMoveData()
Dim lRow As Integer, i As Integer, j As Integer
'Find last roe in Sheet1
With Worksheets("Latency")
lRow = .Cells.SpecialCells(xlLastCell).Row
j = 1
For i = 1 To lRow
If UCase(.Range("E" & i)) = "COMPATIBLE" And UCase(.Range("O" & i)) = "Pass" Then
.Range("M" & i).Copy Destination:=Worksheets("TP").Range("D" & j)
j = j + 1
End If
Next
End With
End Sub
UCase(.Range("O" & i)) = "Pass"
Will always be false :-)
You are never going to match UCase(Cell) = "Pass", right? You either need to have:
UCase(.Range("O" & i)) = "PASS"
or
.Range("O" & i) = "Pass"
Try this
Sub sbMoveData()
Dim lRow As Integer, i As Integer, j As Integer
Dim ws1, ws2 As Worksheet
Set ws1 = ThisWorkbook.Sheets("Latency")
Set ws2 = ThisWorkbook.Sheets("TP")
'Find last roe in Sheet1
lRow = ws1.Cells.SpecialCells(xlLastCell).Row
j = 1
For i = 1 To lRow
If ws1.Range("A" & i) = "COMPATIBLE" And ws1.Range("B" & i) = "Pass" Then
ws1.Range("M" & i).Copy Destination:=ws2.Range("D" & j)
j = j + 1
End If
Next i
End Sub