How to insert a function into my code block - excel

I would like to insert a function in this vba code block. What I am trying to do here is look for matches based on two criteria. The first criteria is their ID(Col:B) and the second is the amount they paid (Col:C). The idea being that the function would then cycle through the columns referenced and return the index function until the end of the data.
The VBA code block is listed below:
Update:
Last = Cells(Rows.Count, "B").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "B").Value) > 0 And (Cells(i, "C").Value) > 0 Then
Cells(i, "D") = "=IFERROR(INDEX($F$2:$F$500,MATCH(1,INDEX((B2 = $G$2:$G$500)*(C2=$H$2:$H$500),0,1),0)), No Match)"
End If
Next i
This will now paste the function into the cells. I am having two problems:
I need to figure out how to use a range in the "D" column so that it adjusts the cell selection one down as the function pastes down. I know the "Range" does this, but I want it to stop when there is no longer any data, range will continue past the end of the data to the cell selected for the range end, and the way it's set up now stops at the end of the data.
The "No Match" part isn't transfering over because it has something to do with the quotes. It's reconginizing it as something different than text.

Related

Change countif range using vba

I would like to change the range in the counfif-formula by using vba. By clicking a button the Range A3:A3 changes to A3:A4, then clicking the button again A3:A4 changes to A3:A5, and so on... I managed to create a constant vba-formula, but I do not know how to proceed. Anybody? :)
Excel
VBA
I think you need a dummy cell/value to achieve, The dummy cell will "count" how many times you have clicked the button. My solution will require a dummy cell.
Sub Countif_function()
Dim start_value As Long
start_value = Cells(1, "D").Value 'Take the value from cell D1
If start_value <= 3 Or Cells(1, "D").Value = "" Then start_value = 3 ' If the value is less than 3 (since you start at row 3) or if the value is empty then set the minimum start value to 3 (otherwise countif will fail).
Cells(3, "D").Value = Application.WorksheetFunction.countif(Range(Cells(3, "A"), Cells(start_value, "A")), "A") 'Using the inbuild function in VBA to retrieve the countif
Cells(3, "E").Value = "=COUNTIF(A3:A" & start_value & ",""A"")" 'Just for view purpose of the final formula result, can be ignored in the code
Cells(1, "D").Value = Cells(1, "D").Value + 1 'Add +1 row. This will be used as the start value for next time the code is executed.
End Sub
I used "A" as criteria since you used it in your example code, but could be change to "C3" or some other cell.
Result:
The alternate if you don't want a dummy cell to count as per the previous answer is to read the formula in the cell into a string variable via range.formula, split it on the comma, read the last digit of the first entry in the split array, increment by 1. (If the digit is a 9, then check whether the preceding character is a number or letter, if letter, make the 9 a 10, if number, increment that by one and make the 9 a 0, if the preceding character is also a 9, recursive call). So, yeah, unless you really cannot have the dummy cell, use that solution, it's easier.

Best way to run macro for over 500K rows?

I have a file with a bunch of rows that contains data for certain part numbers from different configurations. Some of these part numbers are repeated throughout the file, and in those duplicated part numbers may contain certain data and some may not. I am trying to find the best way to determine the commonalities in the file for certain data. So for the commonalities, if one row has a value and another row is blank, the value for the nonblank row would be put into the blank row. And if the data on those two rows were different it would change the font color on the cell indicating that this part number two different unique values and should be checked.
Dim i, j, n As Long
Dim lr As Long
Dim moaf As Workbook
Dim sht As Worksheet
Application.ScreenUpdating = False
Set moaf = Workbooks("MOAF3.xlsb")
Set sht = moaf.Worksheets("Wire Data")
n = InputBox("What column # are you trying to fill in?: ")
lr = Cells(Rows.count, 2).End(xlUp).Row
For i = 2 To lr
lkup = Cells(i, 2).Value 'sets first lookup value
Fill = Cells(i, n).Value 'sets the first data value to compare
If Len(Fill) > 0 Then
For j = 2 To lr
lkup2 = Cells(j, 2).Value 'sets the second lookup value
Fill2 = Cells(j, n).Value 'sets the second value to compare
If lkup2 = lkup Then 'checks to see if p/ns are same
If Len(Fill2) = 0 Then 'checks to see if second value is blank
Cells(j, n).Value = Fill 'if value is blank the cell takes value of non blank cell
ElseIf Fill <> Fill2 Then 'checks to see if the values are non matching and non zero
Cells(i, n).Font.ColorIndex = 3 'changes font color of two cells
Cells(j, n).Font.ColorIndex = 3 'changes font color of two cells
End If
End If
Next j
End If
Next i
Application.ScreenUpdating = True
End Sub
Doing this generally freezes my excel, where my computer has 32GB of RAM and is Windows10. Is there a better approach for my problem, or is it something that can be done without using a vba? I've done some research on a method without using vba, but with like sumifs, countifs but haven't really done any deep dives.
So, if I understand your question correctly, you start with following data:
ID Column_header
2 a
3 _BLANK_
4 _BLANK_
5 b
6 _BLANK_
And you want to turn this into:
ID Column_header
2 a
3 a
4 a
5 b
6 b
I know a very simple trick for that (I have put everything in column 'A' for explanation):
Select every cell inside that column
Goto (Ctrl+G) Special, Blanks
In the formula bar, type =A2 (you are currently located in 'A3', and you want to copy there the value of the cell just above it)
Press Ctrl+ENTER
You'll see that 'A2' gets copied into 'A3', 'A3' into 'A4' and 'A5' into 'A6' (the fact that this is done for all blank cells, is due to the Ctrl+ENTER).
Record this into a macro, and it will go much faster.
I already see a question popping up : "Ok, but what about the font colour I want to change?". Well, the newly filled cells are based on a formula, so the length of =FORMULATEXT() won't be zero. You use this as a basis for conditional formatting.
Good luck
The inner for loop just needs to start at i, that is:
for j = i to lr
This should roughly half the runtime.
Further performance enhencements:
Use .Value2 instead of .Value property.
Or even better, read in the entire columns into an array, work on that in VBA, then write the result back.

How to loop through "blocks" of rows in excel dataset instead of each row individually (VBA)?

I'm trying to loop through a data set in Excel where the deciding factors for each block of rows is based on the second column (WO#) because I want to perform calculations (not part of this question) just for items in the same grouping.
Visualization on iterating through blocks of rows
Sub test()
Dim totalHrs As Integer, lastRow As Integer
Dim block As Range, dataSet As Range
lastRow = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
Set dataSet = Range("A2:G" & lastRow)
For Each block In Columns("B").SpecialCells(xlCellTypeConstants, xlTextValues).Areas
'Do calculation stuff
Next block
End Sub
Right now I am having trouble defining what range 'block' should be since it will constantly change. A block begins when the "WO#" column is one number, and the block ends once that column's value changes to something else. How do I go about defining this range?
An easier way would be to just extract rows that have the same WO# into a new table and do the calculations there, but since this dataset can get quite large I would prefer not to do that as the macro will become way too heavy
What if you added this into G11 =IF(B11=B12,F12,C11) and then in F11 you added =G11-E11 and then dragged them down.
It should check to see if it's the last entry in the set and then set itself to the delivery date or set itself equal to the start day of the line below.
Edit:
Try replacing the F11 cell with this =WORKDAY(G11,-E11) and dragging down to account for workdays. To account for holidays, you can create a second sheet to list them out as dates and then add them as a range for the third, optional, argument in the Workday function. Please see the Workday documentation if more clarification is required on the function's use.
The code below defines blocks of similar values in a column. To use this code, either add a column to your worksheet combining columns B and C into one value (=B11&C11) or edit the code to check two columns (If rngCell.Offset(0, 0).Value = rngCell.Offset(1, 0).Value And rngCell.Offset(0, 1).Value = rngCell.Offset(1, 1).Value Then). Also, expand the blocks to more than one column.
'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
'Checks whether a cell's value equals the cell below it.
If rngCell.Value = rngCell.Offset(1, 0).Value Then
Set rngBlock = Union(rngBlock, rngCell.Offset(1, 0))
'If equal, includes the cell below in the block.
Else
'If not equal, does something with the block...
Debug.Print rngBlock.Address
'Starts the next block with the cell below.
Set rngBlock = rngCell.Offset(1, 0)
End If
Next rngCell

Excel VBA count cells until a date is found

I've got an amount of data copied from a table in a .pdf that when pasted into excel puts it all into one column. There are actually multiple pages each with it's own table (the data is one continuous long table split over multiple pages more accurately) and at the top of each page is a series of lines that I'm not interested in (the same unwanted data is at the top of each page). What I am interested in is re-sorting the data under the headers as it is in the table on the original .pdf document, removing the headers in the process. The data as it has been pasted into one column essentially is a list of items in plain text for x rows, followed by a list of start dates for x rows, and then a list of end dates for x rows, repeated every page.
I've figured out how to count the number of lines I don't want by getting a macro to look for the first piece of data I'm interested in ("AAAA") starting at cell (B2);
Cells(2, 2).Select
For i = 1 To 50
If ActiveCell = "AAAA" Then
Exit For
End If
ActiveCell.Offset(1, 0).Select
Next i
Cells(2, 3) = i
If i = 51 Then
Range("B3") = "Cannot find data"
End If
Which starts a search at cell (B2) looking downwards until it finds "AAAA" it then prints how many rows it has moved downwards to find it in cell (C2).
I now wish to be able to start at the cell it has just found [(B34) in this case] and count downwards until it finds the first cell containing a date.
Ultimately I'll need to then count down the same number of cells to find the associated end date and print them all in one row, continuing for the entire column of data.
If anybody could help me with being able to start at the first cell "AAAA" and then count downwards until a date is found, that would be really helpful.
My biggest challeng is to understand what you want to be true. I tryed to make a list of the things what you want.
You have a PDF that when paste in Excel it transform all the
document in one column.
There is a header in each of the Excel pages that you want to delete.
After you find a header you want to find two dates, and they have the same distance from the header.
How I would do it:
For iCounter = 1 to Cells(1048576, 1).End(xlUp).Row
If Cells(iCounter,1) = "YOUR HEADER HERE" then
For kCounter = iCounter to Cells(1048576, 1).End(xlUp).Row
If IsDate(Cells(kCounter,1)) = true then
initialDate = Cells(kCounter,1)
endDate = Cells(2*kCounter-iCounter,1)
End if
Next kCounter
End if
Next iCounter
The following piece of code starts in cell A1 and searches downward until it finds a cell containing a date value. The code only searches until it reaches the last record in the first column (to avoid searching all the way down to the bottom of the sheet if no date is found).
Sub FindFirstDate()
Dim i As Long
For i = 1 To ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
If IsDate(ActiveSheet.Cells(i, 1).Value) = True Then Exit For
Next i
MsgBox "The first cell with a date is " & ActiveSheet.Cells(i, 1).Address
End Sub
In this example the address of the cell with the first date in returned in a MsgBox.

VBA convert all cells in column to type text

I'm using VBA to do some further formatting to a generated CSV file that's always in the same format. I have a problem with my For Each Loop. the loop deletes an entire row if there is more than one blank row which can be determined from the first column alone.
Dim rowCount As Integer
For Each cell In Columns("A").Cells
rowCount = rowCount + 1
'
' Delete blank row
'
If cell = "" And cell.Offset(1, 0) = "" Then
Rows(rowCount + 1).EntireRow.Delete
spaceCount = 0
End If
Next
At some point the value in the loop one of the calls does not have a value of "", it's just empty and causes it to crash. To solve this I think that changing the type of the cell to text before that compare would work but I can't figure out how (no intellisense!!!)
So how do you convert a cell type in VBA or how else would I solve the problem?
Thanks.
Use cell.Value instead of the cell.Text as it will evaluate the value of the cell regardless of the formating. Press F1 over .Value and .Text to read more about both.
Be carefull with the statement For Each cell In Columns("A").Cells as you will test every row in the sheet (over a million in Excel 2010) and it could make Excel to crash.
Edit:
Consider also the funcion TRIM. It removes every empty space before and after a string. If in the cell there is a white space " "; it will look like empty for the human eye, but it has a space inside therefore is different than "". If you want to treat it like an empty cell, then try:
If Trim(cell.value) = "" then
As #andy (https://stackoverflow.com/users/1248931/andy-holaday) said in a comment, For Each is definitely the way to go. This even allows for there to be spaces in between lines.
Example code:
Sub ListFirstCol()
Worksheets("Sheet1").Activate
Range("A1").Activate
For Each cell In Application.Intersect(Range("A:A"), Worksheets("Sheet1").UsedRange)
MsgBox (cell)
Next
End Sub
Thanks Andy!

Resources