I am trying to do an incremental numbering in Excel, but for a specific condition. If the condition does not match, then it should keep the existing cell details.
Image:
As you can see from the picture, I want to create a numbering list in column B, which is based off information shown in the corresponding row in column D. So on the second row, I would the counting to start at "1" and then continue to expand only as the count of "is_null" and "equal" grows. At the same time, I want it to skip over the green and blue cells and keep the contents as is.
As of right now, I have done the following formula:
=COUNTIF($D$1:D2,"is_null")+COUNTIF($D$1:D2,"equals")
This does the proper numbering, however it over-writes the green and blue cells instead of keeping them as "A" and "stop" respectively.
If someone can help me with that issue, then I should be good to go. Thanks!
I'm not sure what the value should be if in Column D you find the String "set_path" or "stop", but if these are always the same (i.e. "a" if Column D = "set_path" and "stop" if Column D = "stop"), you could achieve your desired results with the following formula:
=IF(AND(D2<>"set_path",D2<>"stop"),COUNTIF($D$1:D2,"is_null")+COUNTIF($D$1:D2,"equals"),IF(D2="set_path","a",IF(D2="stop","stop","")))
UPDATE:
Using VBA you could leave the contents of the cell as they are without overwriting them with a formula using the code below:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set the worksheet you are working with, amend as required.
Dim LastRow As Long, i As Long
LastRow = ws.Cells(ws.Rows.Count, "D").End(xlUp).Row
'get the last row with data on Column D
For i = 2 To LastRow
If ws.Cells(i, "B").Value = "" Then 'if cell is empty add formula
ws.Cells(i, "B").FormulaR1C1 = "=COUNTIF(R1C4:RC[2],""is_null"")+COUNTIF(R1C4:RC[2],""equals"")"
End If
Next i
End Sub
Second Update:
I've now adapted the formula to increment the letters by one if "set_path" is found in Column D (Please bear in mind that this will go from A-Z and then it will start going through symbols as per ASCII table, so you might have to amend this if you want alternate behavior):
=IF(AND(D2<>"set_path",D2<>"stop"),COUNTIF($D$1:D2,"is_null")+COUNTIF($D$1:D2,"equals"),IF(D2="set_path",IFERROR(CHAR(COUNTIF($D$2:D2,"set_path")+64),""),IF(D2="stop","stop","")))
Related
I have no experience with VBA and would love some help. As the title indicates, I'm looking for a script that fills a certain number of blank cells within column G with the average of all nonblank cells within that same range (e.g. fill all blank cells in G16:G59 with the average of all nonblank cells within G16:G59).
To make things more complicated, I'd need to vary the range somewhat dynamically as I wouldn't be sure as to how many rows I'd need to apply this script to and a work colleague who would be using this script might not have any experience with VBA either... The easiest solution I can think of is to have another cell contain the name of the last row in the range, or something like this: "Fill all blank cells in G16:Gx with the average of all nonblank cells within G16:Gx, where x = the row name listed in cell G12". Within G12 I'd have a text that states the last row to define the range, e.g. cell G12 contains the text "G80", which makes the range within the script to read G16:G80.
I know I'm asking for a lot, so if you can even just provide guidance on the first bit, I'd be very grateful! Thank you in advance for your time.
I think we can all remember what it was like when we first started out with VBA coding – which is why I’m helping you here. Normally, you’d be lucky to get any assistance with your question without providing at least some code & a description where it wasn’t doing what you wanted. Using the Record Macro button is always a good place to start.
Assumptions made here are that the data is on “Sheet1” in your file, and that there is a value in the last cell in Column G in the range you’re interested in. If that isn’t the case, let me know and I’ll show an alternative method to find the last row.
I’ve added descriptions about what (most) code does in each case to help you understand what’s going on. Let me know how you go with it.
Option Explicit '<~~ get in the habit of putting this at the top of your code
Sub FillInBlanks()
Dim ws As Worksheet '<~~ declare all variables
Dim LastRow As Long '<~~ use Long not Integer
Dim cel As Range '<~~ use intuitive variable names
Dim avg As Double '<~~ Double used here if you want decimal places in the average
Set ws = Sheets("Sheet1") '<~~ be explicit with sheet references
'Best practice to determine last used row in column
LastRow = ws.Cells(Rows.Count, "G").End(xlUp).Row '<~~ finds the last used row in column G
'Calculate the average & assign it to the variable "avg"
avg = Application.Average(ws.Range("G16:G" & LastRow))
'Loop through each cell in the defined range - one at a time
For Each cel In ws.Range("G16:G" & LastRow)
If cel = "" Then '<~~ = If the cell is blank, then...
cel = avg '<~~ ...put the average value (avg) in the cell
cel.Font.Color = RGB(51, 102, 0) '<~~ change color to suit
End If
Next cel '<~~ go to the next cell in the range
End Sub
Need a little help here.
In the "Data" Tab I want to copy values in column "c2:c1000" and paste in column "a1" of another Tab.
This is what i have so far,
Dim x As Long
Dim lastRow As Long
lastRow = Worksheet("Data").Cells(3, Columns.Count).End(xlUp).Column
For x = 1 To lastRow
If Worksheets("Sheet2").Cells(2, "A") = "" Then
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Range(1, "A")
Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
Else
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Cells(2,
Columns.Count).End(xlToLeft).Offset(, 1)
'Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss") --> can't figure how to increment this as this will need to be on the subsequent empty column
End If
Next
End Sub
Your help will be greatly appreciated!
Thank you.
Pasting values first into range A1 and down and then next time to cell B1 and so on, leaves no space for the timestamp to A1, B1 etc. So, I assume that you would like to paste the random values to row 2. So cells A1, B1, ... are left for the timestamp.
Inside the With statements we can refer to properties of the wsAudit so we can replace the "Worksheets("Audit")." reference with just "."
The column.count expression just checks the amount of columns in the worksheet.
The expression .Cells(2, Columns.Count) just points to last cell in the row 2.
The .End(xlToLeft).Column then looks from this column to left and is supposed to find the last not empty cell on this row. It's basically the same idea that in Excel's sheet you would go to cell XDF2 and hit CTRL+Arrow Left from keyboard.
But instead of activating the cell we just want to get the columns index number and then add 1 (the new column) and save it into variable. Now the new column is known.
The expression Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value is really the same as e.g. Range("B2:B1000"), but with this we can use the row and column index numbers instead. This is useful as the column number varies.
And as Samuel pointed out the copy paste operation can be avoided by setting the areas equal.
Dim wsAudit As Worksheet
Dim newColAudit As Long
Set wsAudit = Worksheets("Audit")
With wsAudit
newColAudit = .Cells(2, Columns.Count).End(xlToLeft).Column + 1
Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value = Worksheets("Data").Range("C2:C1000").Value
.Cells(1, newColAudit).Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
End With
Much like your LastRow* variable for your source sheet, create a LastColumn variable for your destination sheet, which will find the last used column the same way you are finding your last used row.
Like so:
Dim LastColumn As Long
LastColumn = Sheets("Audit").Cells(1, Columns.Count).End(xlToLeft).Column
Then use the variable like so:
Destination:= Worksheets("Audit").Cells(1, LastColumn)
It seems that your code contradicts your question too, in your question you explained the data will be written to the Audit sheet in row 1, using the next column each time but your code looks for values in row 2 in your If statement:
If Worksheets("Audit").Cells(2, "A") = "" Then is the same as If Worksheets("Audit").Range("A2") = "" Then.
If you mean to check the first row, change the 2 to 1.
To help improve your codes efficiency:
(Also see the link to 'how to avoid select' in that question):
You can achieve 'copy/paste' without actually using the 'copy' and 'paste' methods by assigning the value of one range to the other, as example, like so:
Worksheets("Audit").Cells(1, LastColumn).Resize(999, 1) = Worksheets("Data").Range("c2:c1000").Value
Note: Change the Resize Property rows to suit the source range (in this case you are wanting to move values from C2:C1000).
*The LastRow variable is a bit confusing, as it is looking for the last used column in row 3.
If it's meant to find a column, consider renaming it to avoid confusion later on in debugging.
If it's meant to find the last row, try like this:
LastRow = Worksheet("Data").Cells(Rows.Count, 1).End(xlUp).Row
My issue now is with my conditional formatting rules - they are working as expected up to a certain point in column W, which is where the cells should be either highlighted or passed over. I have included a set of data from column W and column Z (the reference data) where there are matches that are not being highlighted. In the Column W data I have bolded the numbers that should be highlighted.
Column Z - Z506-Z550
233892
233899
959460
156311
515114
549794
562793
372953
230659
230717
2051205586
364834
790760
334588
538149
288261
19326
267428
Net 90
473853
3211221994
264556
260798
156271
509597
2211211506
800990
597593
431759
377289
224118
178966
276840
430269
431923
431986
547439
512399
234975
512203
602547
443537
376759
284287
608745
Column W - W1144-W1155
233892
367164
368384
344813
233899
233899
233895
-
233917
284287
376759
443537
The conditional formatting formula I have is =VLOOKUP($W4,$Z4:$Z922,1,FALSE) 'Applies To' =$W$4:$W$3600
I am not sure what is causing this conditional formatting to fail here.
Ignore below - now working with conditional formatting instead of vba
I am trying to automate a manual process of cross referencing data and highlighting a cell if the contents are found anywhere in another column of data. However, the amount of data in both of these columns is not the same. And unfortunately, the column I need to loop through and check each cell often has either blank cells or cells that are dashed ("---").
I started with conditional formatting but it was not working properly so I am now on VBA.
Private Sub Workbook_Open()
Dim LastRow As Long
Range("W4").Select
LastRow = Range("W4").End(xlDown).Row
Do Until ActiveCell.Row = LastRow
If Not IsEmpty(Application.Match(ActiveCell.Value, Range("Z:Z"), 0))
Then
ActiveCell.Interior.Color = vbGreen
ActiveCell.Offset(1, 0).Select
End If
Loop
End Sub
Right now the code has a couple issues. It is not finding the last row correctly - when debugging it shows as 65, but should be 3,535 in the test case I'm using. Additionally, my match statement is not working, as it is highlighting every cell instead of only those whose content is found in column Z. And, it highlights up to row 410, which means my Do Until loop must be wrong as well.
After figuring out the one column I eventually need to allow checking columns AA and AB for content matches.
Thanks!
As mentioned in the comment, conditional formatting is the way to go. I just tried the following as a conditional format.
=VLOOKUP($A2,$C$2:$C$7,1,FALSE)
A column of numbers in A as the numbers to be cross-referenced, and a list in column C which are the numbers to be checked. It works perfectly.
I recommend to use Conditional Formatting. The following is just to explain what was wrong with your code:
Always define which worksheet you mean and avoid using Select in Excel VBA.
Using End(xlDown) will find the next free cell (not the last used). Instead use End(xlUp) from the very last cell of the worksheet.
Application.Match does not return a cell but a row number. Therefore IsEmpty does not work.
In the end something like this should work:
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("SheetName")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "W").End(xlUp).Row
Dim MatchRow As Variant
Dim iRow As Long
For iRow = 4 To LastRow
MatchRow = 0 'initialize
MatchRow = Application.Match(ws.Cells(iRow, "W").Value, ws.Range("Z:Z"), 0)
If MatchRow > 0 Then
ws.Cells(iRow, "W").Interior.Color = vbGreen
End If
Next iRow
Note that Contidional Formatting would be a much better solution.
I have an Excel table with a sheet "CR" where columns A to K are filled, with the first row being a header row.
Rows 1-1000 are formatted (borders) and column A contains a formula to autonumber the rows when data in column F is entered.
Sheet "CR" is protected to prevent users from entering data in column A (locked).
Using the Workbook_BeforePrint function, I'm trying to set the print area to columns A to K and to the last row of column A that contains a number.
My code (in object 'ThisWorkbook') is as follows:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ThisWorkbook.Sheets("CR")
' find the last row with data in column A
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
ws.PageSetup.PrintArea = ws.Range("A1:K" & lastRow).Address
End Sub
However, when I click File -> Print, the range of columns A to K up to row 1000 is displayed instead of only the rows that have a number in column A. What am I doing wrong?
Change:
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
To:
lastRow = [LOOKUP(2,1/(A1:A65536<>""),ROW(A1:A65536))]
.End(...) will act like ctrl + arrow-key. if the cell has a formula (which looks empty due to the formula, then it will still stop there... another way would be the use of evaluate (if you do not want to loop) like this:
lastRow = .Evaluate("MAX(IFERROR(MATCH(1E+100,A:A,1),0),IFERROR(MATCH(""zzz"",A:A,1),0))")
This will give the last row (which has a value in column A).
Also check if there are hidden values (looking empty due number format or having characters you can't see directly. Try to go below row 1000 in column A (select a cell after A1000 in column A) and hit ctrl+up to validate where it stops (and why).
EDIT:
(regarding your comment)
"" still leads to a "stop" for the .End(...)-command. So either use my formula, translate the formula into vba or loop the cells it get the last value. Also Find is a good tool (if not doing it over and over for countless times).
lastRow = .Cells.Find("*", .Range("B1"), xlValues, , xlByColumns, xlPrevious).Row
Hi guys this is my first post, I'm wondering if you can possibly assist me.
I'd like to write a macro / script that will allow me to put a formula into the column to the right of the currently selected one (for all active rows of the current column) based on what column I've selected. The issue I'm having is that I don't always know the current column letter (as my selection changes from worksheet to worksheet).
To give you an example:
One of my columns currently contains dates, that dates are entered in different formats though, some are separated with ".", some with "-", some with spaces and so on. I have a formula that will deal with this so I need to put this formula in the column to the right of the selected column (which has the dates).
I have been able to do this when I specify the column letter, but not if it changes.
Please can you help?
Give this a go,
Sub SomethingNeat()
Dim rng As Range, x
x = Selection.Column
On Error Resume Next
Set rng = Columns(x).SpecialCells(xlCellTypeConstants, 23)
If Not rng Is Nothing Then rng.Offset(, 1) = "'=MyFormula"
End Sub
You can use ActiveCell.Offset(0,1).Value = Variable
That means that whetever your current cell is you can move and "select" to put a value to the right cell of the one you have activated. You can move the selection using a loop.
Do
Workbooks("Yur workbook name").Worksheets(1).Range(Adress you want to start adding).Offset(0, 1).formula = "=FORMULA"
i = i + 1
ActiveCell.Offset(1, 0).Activate
Loop While i <= max_row
Edit: 2nd
Put the formula in a cell lets say C1
'Select a range
Set take = Worksheets(1).Range("C1")
take.Copy 'copy the formula
Worksheets(1).Paste Destination:=Worksheets(1).Range("B1:B10")
That will copy your function whenever you want it to