Fill Up in VBA with Value from Below - excel

I have the code below to fill all blank cells in a selection with the value from above. I am trying to do the opposite, fill up based on the value below. I think I need to make it loop from the bottom of the selection but don't know how to do that. See below for the result I am going for.
1 1
2
2 2
3
3
3 3
Sub FillDown()
Dim columnValues As Range, i As Long
Set columnValues = Selection
For i = 1 To columnValues.Rows.Count
If columnValues.Cells(i, 1).Value = "" Then
columnValues.Cells(i, 1).Value = columnValues.Cells(i - 1, 1).Value
End If
Next
End Sub

Iterate from the bottom up: For i = columnValues.Rows.Count to 1 Step -1
And change the columnValues.Cells(i - 1, 1).Value to columnValues.Cells(i + 1, 1).Value
Sub FillDown()
Dim columnValues As Range
Set columnValues = Selection
Dim i As Long
For i = columnValues.Rows.Count To 1 Step -1
If columnValues.Cells(i, 1).Value = "" Then
columnValues.Cells(i, 1).Value = columnValues.Cells(i + 1, 1).Value
End If
Next
End Sub
Before:
After:

I would do this slightly differently and populate ranges at a time rather than cells at a time:
Sub FillUp()
Dim CurrRow As Long, FillRow As Long, LastRow As Long
CurrRow = 1
LastRow = Range("A" & Rows.Count).End(xlUp).Row
Do Until CurrRow >= LastRow
If Not IsEmpty(Range("A" & CurrRow + 1)) Then
CurrRow = CurrRow + 1
Else
FillRow = Range("A" & CurrRow).End(xlDown).Row - 1
Range("A" & CurrRow & ":A" & FillRow).Value = Range("A" & CurrRow).Value
CurrRow = FillRow + 1
End If
Loop
End Sub
Using the .end property of a cell reference will allow you to do that which means you are posting less times to the sheet, this will make a big difference in performance if there are large volumes of data or if there are many calculations in the sheet

I know that Scott Craner already knows this but since an answer was posted making reference to optimize performance, I'm pretty sure this is the best approach.
Dim myArray(), i As Long
myArray = columnValues
For i = UBound(myArray) - 1 To LBound(myArray, 1) Step -1
If myArray(i, 1) = "" Then
myArray(i, 1) = myArray(i + 1, 1)
End If
Next i
columnValues = myArray

Related

Sum Values based on Duplicates - VBA

I am looking for a VBA solution to be able to:
Look for duplicated values in column "A" and format. (Possible with the code below)
With each subsequent duplicate found, the code should sum all the values from Columns "J" through "N" on the first value and fill the duplicated cell black (help)
Sub CombineDuplicates()
Dim Cell As Variant
Dim PList As Range
lRow = Worksheets("Material Planning").Cells(Rows.Count, 1).End(xlUp).Row
Set PList = Worksheets("Material Planning").Range("A4:A" & lRow)
For Each Cell In PList
'Checking whether value in cell already exist in the source range
If Application.WorksheetFunction.CountIf(PList, Cell) > 1 Then
'Highlight duplicate values in red color
cRow = Cell.Row
Range("A" & cRow & ":R" & cRow).Interior.Color = RGB(0, 0, 0)
Else
Cell.Interior.Pattern = xlNone
End If
Next
End Sub
Please see the picture for reference. Top is unfiltered data and the bottom is how it should look after the macro runs. Please let me know if you need any more information. Thanks in advance!
This uses a dictionary to detect duplicates and a class to keep your data organized
Place this piece inside of a class module:
Option Explicit
Private data As datasum
Private prow As Long
Private ptargetsheet As Worksheet
Private Type datasum
thirtyday As Long
threemonth As Long
expectedusage As Double
ordertarget As Double
stock As Long
avgdayleft As Long
dayleft As Long
pending As Long
End Type
Sub initialize(targetsheet As Worksheet, row As Long)
Set ptargetsheet = targetsheet
prow = row
End Sub
Sub addData(dataArray As Variant)
data.thirtyday = data.thirtyday + dataArray(1, 1)
data.threemonth = data.threemonth + dataArray(1, 2)
data.expectedusage = data.expectedusage + dataArray(1, 3)
data.ordertarget = data.ordertarget + dataArray(1, 4)
data.stock = data.stock + dataArray(1, 5)
data.avgdayleft = data.avgdayleft + dataArray(1, 6)
data.dayleft = data.dayleft + dataArray(1, 8)
data.pending = data.pending + dataArray(1, 9)
End Sub
Sub placeData()
With ptargetsheet
.Cells(prow, 6).Value = data.thirtyday
.Cells(prow, 7).Value = data.threemonth
.Cells(prow, 8).Value = data.expectedusage
.Cells(prow, 9).Value = data.ordertarget
.Cells(prow, 10).Value = data.stock
.Cells(prow, 11).Value = data.avgdayleft
.Cells(prow, 13).Value = data.dayleft
.Cells(prow, 14).Value = data.pending
End With
End Sub
And this piece in either your sheet module or a regular module:
Option Explicit
Sub CombineDuplicates()
Dim i As Long
Dim lRow As Long
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim data As DataClass
With Sheets("Material Planning")
lRow = .Cells(.Rows.Count, 1).End(xlUp).row
For i = 4 To lRow
If Not dict.exists(.Cells(i, 1).Value) Then
Set data = New DataClass
data.initialize Sheets("Material Planning"), i
data.addData .Range(.Cells(i, 6), .Cells(i, 14)).Value
dict.Add .Cells(i, 1).Value, data
Else
dict(.Cells(i, 1).Value).addData .Range(.Cells(i, 6), .Cells(i, 14)).Value
dict(.Cells(i, 1).Value).placeData
.Range(.Cells(i, 1), .Cells(i, 14)).Interior.Color = RGB(0, 0, 0)
End If
Next i
End With
End Sub
This would be a simple, but probably not the fastest way of doing it:
Sub CombineDuplicates()
Dim Cell As Variant, PList As Range
Dim i As Long, j As Long, a As Long
Dim k(7) As Long
LRow = Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row
For i = 4 To LRow
Erase k
If Not Range("A" & i).Interior.Color = RGB(0, 0, 0) Then
For j = i + 1 To LRow
If Range("A" & i).Value = Range("A" & j).Value Then
For a = 0 To 7
k(a) = k(a) + Cells(j, a + 2)
Next a
Range("A" & j & ":N" & j).Interior.Color = RGB(0, 0, 0)
End If
Next j
For a = 0 To 7
Cells(i, a + 2) = Cells(i, a + 2) + k(a)
Next a
End If
Next i
End Sub
Essentially, for each row that isn't black (to avoid unnessecary calculaitons) we loop the rest of the range to look for duplicats. Add the values in the array k and keep looking.
Then we end the subloop by adding the number from the array to the current row, and keep going.
Should probably add something to clear the interior formatting first, for subsequent runs.
So after sitting and brainstorming for a while, I figured that I was trying to overcomplicate things. Thanks to your responses it helped me figure out the direction that I wanted to go. This is the current code that I have which is working flawlessly! It is a little slow, but since I am not going to be shifting through thousands of data points, its is manageable.
I tried to insert value added comments in the code to show the process:
Sub CombineDuplicates()
Dim Cell As Variant
Dim PList As Range
Worksheets("Material Planning").Unprotect
Set ws = Worksheets("Material Planning")
'set last row of working range
lRow = Worksheets("Material Planning").Cells(Rows.Count, 1).End(xlUp).Row
'Toggle parameter. If any cells in range are not colored then it will execute the macro to add common values
If Range("A4:A" & lRow).Interior.ColorIndex = xlColorIndexNone Then
For i = 1 To lRow
Application.ScreenUpdating = False
Application.EnableEvents = False
'since all of the "duplicate" values are listed near each oter, I just need to compare them one after another
Fst = ws.Range("A" & i)
Snd = ws.Range("A" & i + 1)
If Snd = Fst Then
'saves the Formula from the cell but just adds the value from the current cell to the next one
'this way even if there are more than 2 duplicates, the sum will continue on to the next cell
ws.Range("F" & i + 1).Formula = ws.Range("F" & i + 1).Formula & "+" & ws.Range("F" & i).Value
ws.Range("G" & i + 1).Formula = ws.Range("G" & i + 1).Formula & "+" & ws.Range("G" & i).Value
ws.Range("J" & i + 1).Formula = ws.Range("J" & i + 1).Formula & "+" & ws.Range("J" & i).Value
'The whole Row will be filled black so that it is not considered in the analysis
Range("A" & i & ":U" & i).Interior.Color = RGB(0, 0, 0)
End If
Next
Application.ScreenUpdating = True
Application.EnableEvents = True
Else
'if there is already formatting on any cells in column A, this will remove the filled black formatting from all cells in the range
Range("A4:U" & lRow).Interior.Color = xlNone
ws.Range("F4:N" & ws.Cells(Rows.Count, 6).End(xlUp).Row).FillDown
ws.Range("P4:U" & ws.Cells(Rows.Count, 6).End(xlUp).Row).FillDown
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
Worksheets("Material Planning").Protect
End Sub
Thank you all for your help and advice on this!
Excel has a built-in dedup function. Can you not programmatically copy the 'Simple Description' column at the top to the area underneath, run the dedup on the range containing the copy, then add sumifs to the remaining columns?
The code below creates the bottom table from the top table shown in the picture.
Sub Dedup()
Range("A1:A9").Copy
Range("A12").PasteSpecial
Range("B1:E1").Copy
Range("B12").PasteSpecial
Range("A13:A20").RemoveDuplicates Columns:=1
Range("B13").Formula = "=SUMIF($A$2:$A$9,$A13,B$2:B$9)"
Range("B13").Copy Destination:=Range("B13:E17")
End Sub
Of course, this doesn't maintain the structure with the black rows, but I haven't understood why you need that anyway, since you still have the original table.
And you'll want to do something a little more sophisticated about identifying the correct ranges, particularly for the copied table and when copying the sumif formula from the first cell to the last cell in the range that results from the deduplication. I've kept it simple here for expediency.
Edit: If you want the bottom table to reflect the structure of the original table, you could do a countif on each of the rows in the copy and insert the requisit number of rows that that gives you, and make the new rows black.
Paste Special xlPasteSpecialOperationAdd
This is a slow solution but may be easily understood.
It loops through the cells in column A and uses Application.Match to find the index (position) of the first occurrence. If it is not the same then it colors the row and uses PasteSpecial with xlPasteSpecialOperationAdd to add the found values to the values defined by the index.
Application.ScreenUpdating will speed up the code hiding the on-going 'worksheet dance'.
The Code
Option Explicit
Sub CombineDuplicates()
Dim ws As Worksheet
Dim PList As Range
Dim Cell As Range
Dim ColsAll As Range
Dim Cols1 As Range
Dim Cols2 As Range
Dim cIndex As Variant
Dim lRow As Long
Dim cRow As Long
Set ws = Worksheets("Material Planning")
lRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Set PList = ws.Range("A4:A" & lRow)
Set ColsAll = ws.Columns("A:N")
Set Cols1 = ws.Columns("F:K")
Set Cols2 = ws.Columns("M:N")
Application.ScreenUpdating = False
For Each Cell In PList.Cells
cRow = Cell.Row
cIndex = Application.Match(Cell.Value, PList, 0) + 3
If cIndex < cRow Then
ColsAll.Rows(cRow).Interior.Color = RGB(0, 0, 0)
Cols1.Rows(cRow).Copy
Cols1.Rows(cIndex) _
.PasteSpecial xlPasteValues, xlPasteSpecialOperationAdd
Cols2.Rows(cRow).Copy
Cols2.Rows(cIndex) _
.PasteSpecial xlPasteValues, xlPasteSpecialOperationAdd
Else
ColsAll.Rows(cRow).Interior.Pattern = xlNone
End If
Next
Application.CutCopyMode = False
ws.Range("A3").Select
Application.ScreenUpdating = True
End Sub
Try this code, please. It should be very fast, using arrays and working only in memory and does not need to color anything. The processing result, meaning only the unique values with the necessary sum per each column will be dropped on a new sheet added after the processed one:
Sub CombineDuplicates()
`It needs a reference to 'Microsoft Scripting Runtime'
Dim LROW As Long, arrA, arr, arrR(4), arrF, dict As New Scripting.Dictionary
Dim sh As Worksheet, resSh As Worksheet, i As Long, j As Long, arrFin
Set sh = Worksheets("Material Planning")
LROW = sh.cells(rows.Count, 1).End(xlUp).row
arrA = sh.Range("A4:A" & LROW).value
arr = sh.Range("J4:N" & LROW).value
For i = 1 To UBound(arrA)
If Not dict.Exists(arrA(i, 1)) Then
For j = 0 To 4
arrR(j) = arr(i, j + 1)
Next j
dict.Add arrA(i, 1), arrR
Else
For j = 0 To 4
arrR(j) = dict(arrA(i, 1))(j) + arr(i, j + 1)
Next j
dict(arrA(i, 1)) = arrR
End If
Next i
ReDim arrFin(1 To dict.Count, 1 To 5)
ReDim arrF(1 To dict.Count, 1 To 1)
For i = 0 To dict.Count - 1
arrF(i + 1, 1) = dict.Keys(i)
For j = 0 To 4
arrFin(i + 1, j + 1) = dict.items(i)(j)
Next
Next i
Set resSh = Worksheets.Add(After:=sh) 'add a new sheet aftere the active one and drop the array at once
resSh.Range("A2").Resize(UBound(arrF), 1).value = arrF
resSh.Range("J2").Resize(UBound(arrFin), UBound(arrFin, 2)).value = arrFin
End Sub
This approach will allow running the code as many times you need, after eventual updates or just in case. Otherwise, it will return double dates each next time...
If you have a problem with adding the necessary reference, please run the next code before the one able to process your data:
Sub addScrRunTimeRef()
'Add a reference to 'Microsoft Scripting Runtime':
'In case of error ('Programmatic access to Visual Basic Project not trusted'):
'Options->Trust Center->Trust Center Settings->Macro Settings->Developer Macro Settings->
' check "Trust access to the VBA project object model"
Application.VBE.ActiveVBProject.References.AddFromFile "C:\Windows\SysWOW64\scrrun.dll"
End Sub
Edited:
If you insist to keep all the range, and making black the interior of duplicates, you can try the next code, also very fast. It will also return in a newly created sheet, but only for testing reason. If it does what you want, the code can be easily adapted to overwrite the existing range of the active sheet:
Sub CombineDuplicatesKeepAll()
Dim LROW As Long, arrA, arrR(14), arrF, dict As New Scripting.Dictionary
Dim sh As Worksheet, resSh As Worksheet, i As Long, j As Long, arrFin, firstR As Long
Dim rngCol As Range, k As Long
Set sh = Worksheets("Material Planning")
LROW = sh.cells(rows.Count, 1).End(xlUp).row
firstR = 4 'first row of the range to be processed
arrA = sh.Range("A" & firstR & ":N" & LROW).value 'place the range to be processed in an array
ReDim arrFin(1 To UBound(arrA), 1 To UBound(arrA, 2)) 'set the final array at the same dimensions
For i = 1 To UBound(arrA) 'iterate between the array elements
If Not dict.Exists(arrA(i, 1)) Then 'if not a dictionary key as value in column A:A (array column 1):
arrR(0) = sh.Range("A" & i + firstR - 1).Address 'place the cell address like forst dictionary item array element
arrR(1) = i 'the array second element will be the array row (to update it later)
arrFin(i, 1) = arrA(i, 1) 'first element of the final array, on i row will be the first column value
For j = 2 To 14
arrR(j) = arrA(i, j) 'input the rest of the row values in the array to be the dictionary item
arrFin(i, j) = arrA(i, j) 'place the same values in the final array
Next j
dict.Add arrA(i, 1), arrR 'add the array built above like dictionary item
Else
arrR(0) = dict(arrA(i, 1))(0) 'keep the same call address like the first element of the array to be input as item
arrFin(i, 1) = arrA(i, 1) 'place the value in column A:A in the first column of the final array
arrR(1) = dict(arrA(i, 1))(1) 'keep the row of the first dictionary key occurrence
For j = 2 To 14 'fill the array with the values of all row columns
If j <= 9 Then 'for first 9 columns keep their value
arrR(j) = dict(arrA(i, 1))(j)
Else 'for the rest (J to N) add the existing value (in dictionary) to the cells value
arrR(j) = dict(arrA(i, 1))(j) + arrA(i, j)
End If
arrFin(i, j) = arrA(i, j) 'fill the final array with the row data
Next j
dict(arrA(i, 1)) = arrR 'place the array like dictionary item
If rngCol Is Nothing Then 'if range to be colored does not exist, create it:
Set rngCol = sh.Range("A" & i + firstR - 1 & ":N" & i + firstR - 1)
Else 'if it exists, make a Union between existing and the new one:
Set rngCol = Union(rngCol, sh.Range("A" & i + firstR - 1 & ":N" & i + firstR - 1))
End If
End If
Next i
'adapt te final array rows which used to be the first occurrence of the same dictionary key:
For i = 0 To dict.Count - 1
k = dict.items(i)(1) 'extract the previously memorized row to be updated
For j = 2 To 14 'adapt the row content, for the row range equivalent columns
arrFin(k, j) = dict.items(i)(j)
Next
Next i
'just for testing, paste the result in a new added sheet.
'If everything OK, the code can drop the value in the active sheet
Set resSh = Worksheets.Add(After:=sh)
'drop the array content at once:
resSh.Range("A4").Resize(UBound(arrFin), UBound(arrFin, 2)).value = arrFin
If Not resSh Is Nothing Then _
resSh.Range(rngCol.Address).Interior.Color = vbBlack 'color the interior of the next occurrences
End Sub
I tried commenting the code lines, in a way to be easily understood. If something unclear, do not hesitate to ask for clarifications.
Please, send some feedback after testing it.

How to increment Numbers With Decimals and Restart Numbering When Number Changes?

I want to increment the decimal part of a number and restart numbering every time the number changes as below
1.00
1.01
1.02
1.03
1.04
1.05
2.00 'Restart With 2
2.01
3.00 'Restart With 3
3.01
3.02
3.03
I used the following Code
Sub AutoNumberDecimals()
Dim Rng, C As Range
Dim Lrow As Long
Dim i As Integer
Lrow = Cells(Rows.Count, 1).End(xlUp).Row
Set Rng = Worksheets("Union").Range("A2:A" & Lrow)
For Each C In Rng.Cells
If C.Value = "" And C.Offset(0, 1).Value = "" Then
C.Offset(1, 0).Value = C.Value + 0.01
Next C
End Sub
But It did not work
Appreciate your help
Thanks, Regards
I wrote this code. Make sure to add it in Sheet1 module (or similar sheet). It reacts when you enter a number in column 1 and it renumbers all numbers in that column. If you enter 1, it shows 1.00... if you enter 1 again, it will show 1.01. If you enter 2 you will have 2.00 etc...
Private ChangingValues As Boolean
Private Sub RenumFirstColumn()
Dim RowNo As Integer
Dim Major As Integer
Dim Minor As Integer
Dim CurrentValue As String
RowNo = 1
Major = 1
Minor = 0
Do
CurrentValue = CStr(Cells(RowNo, 1).Value)
If Int(Val(Left(CurrentValue, 1))) = Major Then
CurrentValue = CStr(Major) & "." & Format(Minor, "00")
Minor = Minor + 1
If Minor > 99 Then
MsgBox "To high value (> X.99)"
Exit Sub
End If
Else
Major = Val(Left(CurrentValue, 1))
Minor = 0
CurrentValue = CStr(Major) & "." & Format(Minor, "00")
Minor = Minor + 1
End If
Cells(RowNo, 1).NumberFormat = "#"
Cells(RowNo, 1).Value = CurrentValue
RowNo = RowNo + 1
Loop Until IsEmpty(Cells(RowNo, 1))
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 1 And ChangingValues = False Then
ChangingValues = True
RenumFirstColumn
ChangingValues = False
End If
End Sub
Hope it was what you were looking for
Try the next code, please. It uses maxIncr variable to set a maximum incrementing times:
Sub IncrementingRoots()
Dim sh As Worksheet, lastR As Long, maxIncr As Long
Dim NrI As Long, i As Long, j As Long
Set sh = ActiveSheet: maxIncr = 7
lastR = sh.Range("A" & Rows.count).End(xlUp).Row
For i = 2 To lastR + maxIncr
If sh.Range("A" & i).Value <> "" Then
NrI = sh.Range("A" & i).Value
For j = 1 To maxIncr
If sh.Range("A" & i + j).Value = Empty Then
sh.Range("A" & i + j).Value = sh.Range("A" & i + j - 1).Value + 0.01
Else
i = j + i - 1: Exit For
End If
Next
End If
If i > lastR Then Exit For
Next i
End Sub
And the next code is yours adapted to work. But impossible to procress the last number in the range, too, without something more (like maxIncr in my above code)...
Sub AutoNumberDecimals()
Dim sh As Worksheet, Rng As Range, C As Range, Lrow As Long, i As Long
Set sh = ActiveSheet 'Worksheets("Union")
Lrow = sh.cells(Rows.count, 1).End(xlUp).Row
Set Rng = sh.Range("A2:A" & Lrow)
For Each C In Rng.cells
If C.Value = "" And (C.Offset(1, 0).Value <> _
Int(C.Value Or C.Offset(1, 0).Value = "")) Then
C.Value = C.Offset(-1, 0).Value + 0.01
End If
Next C
End Sub
This uses DataSeries and NumberFormat to fill the cells.
This creates a random board, and isn't necessary to the main code.
Cells.Clear
Cells(1, 1) = 1 ' creates a random board
x = 2
For i = 2 To 20
If Rnd() > 0.8 Then
Cells(i, 1) = x
x = x + 1
End If
Next i
Cells(21, 1) = 0 ' terminates entries
Note that rather than determine the row column length using code, I have preset it to 21, although you can use the terminating 0.00 value to define a column length.
The main code:
Range("a:a").NumberFormat = "0.00"
For i = 1 To 21 ' loops through range
j = 0 ' finds local range
If Cells(i, 1) <> "" And Cells(i, 1) > 0 Then
Do
j = j + 1
Loop While Cells(i + j, 1) = ""
End If
Range(Cells(i, 1), Cells(i + j - 1, 1)).DataSeries Type:=xlLinear, Step:=0.01
i = i + j ' jumps to next entry
Next i
Each cell is formatted into the desired style. Then the loop finds a non-empty cell, and determines the associated local subrange by checking if the next cell down is empty or not, and continues until it isn't. Then the subrange is formatted using DataSeries with a Step of 0.01.
Ref:
https://learn.microsoft.com/en-us/office/vba/api/excel.range.dataseries

How to create a nested loop to check if a value exists in a second list

I am trying to compare values in two lists. I want my code to compare a value in the first list and check all the entries in the second list. If there is a match then the code will print true next to the value in the first list and if not it will print false.
The problem I am having is that my code only compares values that are in the same row.
The code runs and I have tried it on a two smaller lists to make sure the data types are to same and there aren't any extra spaces or commas in the lists that would lead to a "False" output. I have also tried changing the order of the for and if statements but this doesn't work either.
Sub findvalues()
For i = 2 To 16
For j = 2 To 16
If Cells(i, 3).Value = Cells(i, 1).Value Then
Cells(i, 4).Value = "TRUE"
ElseIf Cells(i, 3).Value = Cells(j + 1, 1).Value Then
Cells(i, 4).Value = "TRUE"
Else
Cells(i, 4).Value = "FALSE"
End If
Next j
Next i
End Sub
Here are the two lists I am testing the code on
Slight mods to your code based on the data you provided in columns 1 & 3. As always, things could be improved but this should get you going ...
Sub findvalues()
Dim i As Long, j As Long, bResult As Boolean
For i = 2 To 16
strValueToLookFor = Cells(i, 1)
For j = 2 To 16
bResult = False
If strValueToLookFor = Cells(j, 3).Value Then
bResult = True
Exit For
End If
Next j
Cells(i, 6).Value = bResult
Next i
End Sub
... you may just need to flick the columns over so the first list searches on the second list or vice versa.
I don't see any need for VBA - formulas are the way to go - but to avoid two loops one could do this:
Sub findvalues()
Dim i As Long
For i = 2 To 130
Cells(i, 4).Value = IsNumeric(Application.Match(Cells(i, 1).Value, Range("C2:C130"), 0))
Next i
End Sub
Update: this does not cater for multiple matches.
There are many was to achieve that. one of them is by using IF & COUNTIF
Formula
=IF(COUNTIF($E$2:$E$6,A2)>0,"TRUE","FALSE")
Results:
VBA CODE
Option Explicit
Sub findvalues()
Dim i As Long
Dim rng As Range
With ThisWorkbook.Worksheets("Sheet1") 'Change if needed
Set rng = .Range("A2:A130") 'set rng to includes values from column A, rows 2:130
For i = 2 To 130 'Loop from row 2 to 130
'Check if the values in column C includes in the rng
If Application.WorksheetFunction.CountIf(rng, .Range("C" & i).Value) > 0 Then
.Range("D" & i).Value = "TRUE"
Else
.Range("D" & i).Value = "FALSE"
End If
Next i
End With
End Sub
VBA code to reconcile two lists.
Sub Reconciliation()
Dim endRow As Long
Dim ICount As Long
Dim Match1() As Variant
Dim Match2() As Variant
Dim ws As Worksheet
Set ws = Worksheets("Recon")
ICount = 0
endRow = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row
endRow1 = ws.Cells(ws.Rows.Count, 11).End(xlUp).Row
Match1 = Sheet1.Range("b2:b" & endRow)
Match2 = Sheet1.Range("K2:K" & endRow1)
For i = LBound(Match1) To UBound(Match1)
For j = LBound(Match2) To UBound(Match2)
If Match1(i, 1) = Match2(j, 1) Then
ICount = ICount + 1
Sheet1.Range("C" & i + 1).Value = ICount
Sheet1.Range("L" & j + 1).Value = ICount
Else
End If
Next j
Next i
End Sub

Count string within string using VBA

I have product codes: (they are in C column of active sheet)
DO-001
DO-002
DO-003
DO-004
And I have big list of data: (they are in C column of "Sheet1")
41300100_DO-001_14215171
41300104_DO-001_14215173
K1_ISK_41300661_DO-002_13190369
NP_41533258_DO-003_14910884
DO-003_DD_44_ddd
And I want to count how many times do the product codes appear in the list of data. So the result for this case'd be: (result is H column of active sheet)
DO-001 2
DO-002 1
DO-003 2
DO-004
I have done this with this code:
Sub CountcodesPLC()
Dim i, j As Integer, icount As Integer
Dim ldata, lcodes As Long
icount = 0
lcodes = Cells(Rows.Count, 3).End(xlUp).Row
ldata = Worksheets("Sheet1").Cells(Rows.Count, 3).End(xlUp).Row
For i = 10 To lcodes
For j = 2 To ldata
If InStr(Worksheets("Sheet1").Range("C" & j), Range("C" & i)) <> 0 Then
icount = icount + 1
End If
Next j
If icount <> 0 Then
Range("H" & i).Value = icount
End If
icount = 0
Next i
End Sub
But I want to change it, so if the list of data contains some key words like "NP", "ISK", then not to count them, or if the first part of the data is the code then also not to count them, so the result for this example would be:
DO-001 2
DO-002
DO-003
DO-004
Also, I'll have around 1.000 product codes, and around 60.000 strings of data.
Will my code crash?
Seems your code is OK. But if you want to match only the first part of string (a'ka StartsWith), i'd change only this line:
If InStr(Worksheets("Sheet1").Range("C" & j), Range("C" & i)) <> 0 Then
to:
If Worksheets("Sheet1").Range("C" & j) Like Range("C" & i) & "*" Then
For further details, please see: Wildcard Characters used in String Comparisons
Use Dictionnary
Dim Dict As Scripting.Dictionary
Set Dict = New Scripting.Dictionary
Arr = Split("refer your text here", "_")
For I = LBound(Arr) To UBound(Arr)
If Dict.Exists(Arr(I)) Then
Dict(Arr(I)) = Dict(Arr(I)) + 1 'increment
Else
Dict.Add Arr(I), 1
End If
Next I
This may be OTT for the requirement but should work quite quickly.
Public Sub Sample()
Dim WkSht As Worksheet
Dim LngRow As Long
Dim AryLookup() As String
Dim VntItem As Variant
'We put what we want to search into an array, this makes it a lot quicker to search
Set WkSht = ThisWorkbook.Worksheets("Sheet1")
ReDim AryLookup(0)
LngRow = 1
Do Until WkSht.Range("A" & LngRow) = ""
If AryLookup(UBound(AryLookup, 1)) <> "" Then ReDim Preserve AryLookup(UBound(AryLookup, 1) + 1)
AryLookup(UBound(AryLookup, 1)) = Trim(UCase(WkSht.Range("A" & LngRow)))
LngRow = LngRow + 1
Loop
Set WkSht = Nothing
'Then we go down the list and check the array against each item
Set WkSht = ActiveSheet
LngRow = 1
Do Until WkSht.Range("A" & LngRow) = ""
WkSht.Range("B" & LngRow) = 0
For Each VntItem In AryLookup()
'This looks for the match without any of the exclusion items
If (InStr(1, VntItem, Trim(UCase(WkSht.Range("A" & LngRow)))) <> 0) And _
(InStr(1, VntItem, "NP") = 0) And _
(InStr(1, VntItem, "ISK") = 0) Then
WkSht.Range("B" & LngRow) = WkSht.Range("B" & LngRow) + 1
End If
Next
LngRow = LngRow + 1
Loop
Set WkSht = Nothing
MsgBox "Done"
End Sub
Basically, the 60,000 data strings will go into an array in memory, then the array will be searched against the 1,000 products. Searching in memory should be quick.
One thing I would raise is the exclusion method may produce false positives.
For example, excluding NP will exclude: -
NP_41533258_DO-003_14910884
NPA_41533258_DO-003_14910884
41533258_ANP_DO-003_14910884
You may want to think about the method overall.
Have you considered an array formula, not sure how it will perform vs code, but, you could do something along these lines, where list is in A and prod numbers in B
=SUM(IF(NOT(ISERR(SEARCH(B1 & "_",$A$1:$A$5,1)))*(ISERR(SEARCH("NP_",$A$1:$A$5,1))),1,0))
Where "NP" would be replaced by a range containing the exclusions, I've left as NP to show what's happening.
The code would be like this. But I don't know the speed.
Sub test()
Dim vDB, vLook, vSum(), Sum As Long
Dim Ws As Worksheet, dbWs As Worksheet
Dim s As String, sF As String, sCode As String
Dim i As Long, j As Long, n As Long
Set dbWs = Sheets("Sheet1")
Set Ws = ActiveSheet
With Ws
vLook = .Range("a1", .Range("a" & Rows.Count).End(xlUp))
End With
With dbWs
vDB = .Range("c1", .Range("c" & Rows.Count).End(xlUp))
End With
n = UBound(vLook, 1)
ReDim vSum(1 To n, 1 To 1)
For i = 1 To n
sF = Split(vLook(i, 1), "-")(0)
sCode = Replace(vLook(i, 1), sF, "")
Sum = 0
For j = 1 To UBound(vDB, 1)
s = vDB(j, 1)
If Left(s, Len(sF)) = sF Or InStr(s, "NP") Or InStr(s, "ISK") Then
Else
If InStr(s, sCode) Then
Sum = Sum + 1
End If
End If
Next j
If Sum > 0 Then
vSum(i, 1) = Sum
End If
Next i
Ws.Range("h1").Resize(n) = vSum
End Sub

Sum on vba script from another table

I have to create a vba script that takes data from a column of another table in Excel ("Days Of Last Update", which is decimal), and then makes a sum based if the day are above 2 (showing the result in a new column).
It seems to be very simple, but I am a beginner and have no idea how to proceed.
UPDATE:
Hello everyone, thanks for the help. Now I have a new problem, still in this project. Here is what I've done:
The RawData's sheet have a column named "Days Since Last Update", that tells me when the Service Request of the product is updated. So, I created a new column with this formula =IF(N:N>2,1,0), to tells me if the Days Since Last Updated are above 2. I refreshed my pivot table to get this new column, did a Sum of the data, and get what I previously wanted, but, when the Update function of the worksheet run, the new column of the pivot table, as well as the column of the RawData with the formula, are gone. In the code (that isn't done by me) of the Update function, there was something like this:
Worksheets("Pivot table").PivotTables("PivotTable1").PivotCache.Refresh
It has something to do with my problem?
Please find the sample data sheet appended below.
VBA Code is appended below.
Sub Extract_Values()
Dim wks As Worksheet
Dim startRow As Integer
Dim lastRow As Integer
Dim vArray As Variant
Dim vNewArray As Variant
Dim i As Integer, j As Integer
Dim Counter1 As Integer, Counter2 As Integer
startRow = 2
Set wks = Sheets("Sheet1")
With wks
lastRow = .Cells(Rows.Count, 1).End(xlUp).Row
vArray = .Range("A" & startRow & ":D" & lastRow).Value2
For i = 1 To UBound(vArray)
If vArray(i, 4) = "Y" Then
Counter1 = Counter1 + 1
End If
Next i
ReDim vNewArray(1 To Counter1, 1 To 2)
For j = 1 To UBound(vArray)
If vArray(j, 4) = "Y" Then
Counter2 = Counter2 + 1
vNewArray(Counter2, 1) = vArray(j, 1)
vNewArray(Counter2, 2) = vArray(j, 2)
End If
Next
End With
Range("E" & startRow & ":F" & startRow + Counter1 - 1) = vNewArray
Range("E" & startRow & ":E" & startRow + Counter1 - 1).Select
Selection.NumberFormat = "m/d/yyyy"
Range("F" & startRow + Counter1).Select
End Sub
I am also a beginner setting proper VBA code in cell F8 which I have calculated Excel in-built Count Function.
COLUMN D Contains IF Formula like 'D2=IF(B2>1,"Y","N")'
HTH
Try
Dim RowCount As Integer
Dim NewRow As Integer
RowCount = 2
NewRow = 2
Do Until RowCount > Cells(2, 2).End(xlDown).Row
If Cells(RowCount, 2) > 1 Then
Range(Cells(NewRow, 5), Cells(NewRow, 6)).Value = Range(Cells(RowCount, 1), Cells(RowCount, 2)).Value
NewRow = NewRow + 1
End If
RowCount = RowCount + 1
Loop
End Sub

Resources