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.
Related
I'm trying to search on the specific column(E), and if matched with the first 4 digit, I would like to copy the number to a different column.
Column E is where i would like to paste all the random number(dynamic)
Column A/B/C is static where i would add 4 digits from time to time.
Column I/J/K is where is would like to paste the result.
PS:
I'm doing it manually and would really appreciate if someone can help me out with the automation hence no code is provided. :(
Having ExcelO365 means you may use FILTER(). Therefor try the below:
Formula in I2:
=FILTER($E:$E,ISNUMBER(MATCH(--LEFT($E:$E,4),A:A,0)))
Drag right to K2. Now, this is dynamic and will change accordingly upon data entry in column E:E, or changing values in A:C.
this is the code to execute on sheet 1, it goes through the entire column E and validates through the formula of counting if in each of the first three columns and assigns the value found in the corresponding columns.
Sub macro()
Dim Static_Data As String
Dim Sht As Worksheet
Set Sht = ThisWorkbook.Sheets("Hoja1")
Active_row = 2
Do While Sht.Range("E" & Active_row).Value <> ""
Static_Data = Sht.Range("E" & Active_row).Value
For i = 1 To 3
If Application.WorksheetFunction.CountIf(Sht.Columns(i), Mid(Static_Data, 1, 4)) > 0 Then
Sht.Cells(Sht.Cells(Rows.Count, i + 8).End(xlUp).Row + 1, i + 8).Value = Static_Data
End If
Next i
Active_row = Active_row + 1
Loop
End Sub
For Excel versions that don't support FILTER or as an alternative you can use standard formulas for this.
If you use columns F-H as helper columns (and these columns can be hidden) then the formula in F2 will be:
=IF(NOT(ISERROR(VLOOKUP(VALUE(LEFT($E2,4)),A$2:A$100,1,FALSE)))=TRUE,$E2,"")
The formula can then be copied across and down. This will find your matches.
In order to then remove the blanks from the data you can use the following formula in I2 and again copy across and down. Depending on how many numbers you want to add in, you may want to extend the range A$2:A$100 in the top formula and F$2:F$100 in the bottom formula
=IFERROR(INDEX(F$2:F$100,AGGREGATE(15,6,(ROW(F$2:F$100)-ROW(F$2)+1)/(F$2:F$100<>""),ROWS(I$2:I2))),"")
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
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.
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.
When recording a macro, i want to change the current value of a cell to a new value dependant on the original value. not set it to a "recorded" value.
Example : value in cell needs to change from .1234 (text) to 0,1234 (number). And
.56789 to 0,56789
My work method is:
record macro
"F2" : to change value,
"home" : go to beginning,
"del",
"del",
"0",
",",
"return",
stop record macro
but when i use the macro on other fields, the value is changed to 0,1234 even when original value is .5678 for example.
This can be done in VBA but the most simple solution (if all your values begin with ".") is to use the built in "Text to Columns" Excel option (by simply selecting your entire column then ALT+A, +E, +F). If this has to be done with VBA please tell. Hope this alone helps.
Edit I've wrote this code to solve your problem (based on the rule of the cell values are starting with "." and then switching to numbers is made by adding a "0" to the initial text). Then "number" format is picked up by Excel as implicit format.
Sub ChangeFormat()
For i = 1 To 2 'This is the row index (it's now set to the 1st 2 rows.)
For j = 1 To 2 'This is the column Index (i.e. column 2 is B, 1 is A, etc.) (it's now set to the A and B columns)
If Left(ActiveSheet.Cells(i, j), 1) = "." Then
ActiveSheet.Cells(i, j).Value = "0" & ActiveSheet.Cells(i, j).Value
End If
Next j
Next i
End Sub