I have 30 excel files need to be done with the same operation. I have non-fixed number of rows in my excel files. For all excel files, I want to apply VBA to write IF function into the column"H" from the second row to the last row.
Here is the formula I need to write (for the second row): =IF(AND(C2=C3,C2=C1,G2=TRUE,G1=FALSE,G3=TRUE),"O",IF(AND(C2=C3,C2<>C1,G2=TRUE),"FromYes",IF(AND(C2=C1,C2=C3,G2=TRUE,G1=TRUE,G3=FALSE),"D",IF(AND(C2=C1,C2<>C3,G2=TRUE),"ToTmr",""))))
Other rows shall have similar formula.
Is there any way for me to write a formula directly in vba for the whole column "H"?
I have tried some code like this, but it run extremely slow
For x = 2 To lastRow
If ws.Cells(x, 3) = ws.Cells(x + 1, 3) And ws.Cells(x, 3) = ws.Cells(x - 1, 3) And ws.Cells(x, 7) = True And ws.Cells(x - 1, 7) And ws.Cells(x + 1, 7) = True Then
ws.Cells(x, 8) = "O"
Else
If Cells(x, 3) = Cells(x + 1, 3) And (Cells(x, 3) <> Cells(x - 1, 3)) And Cells(x, 7) = True Then
Cells(x, 8) = "FromYes"
Else
If Cells(x, 3) = Cells(x - 1, 3) And Cells(x, 3) = Cells(x + 1, 3) And Cells(x, 7) = True And Cells(x - 1, 7) = True And Cells(x + 1, 7) = False Then
Cells(x, 8) = "D"
Else
If Cells(x, 3) = Cells(x - 1, 3) And Cells(x, 3) <> Cells(x + 1, 3) And Cells(x, 7) = True Then
Cells(x, 8) = "ToTmr"
End If
End If
End If
End If
Next x
Next d
There are a few things to change in your approach. Firstly, use the fact that you can set cell formulas with VBA. Secondly, use the fact that you can set cell formulas for an entire range in VBA. Thirdly, if your formula is fast, use it instead of code (this is not a hard and fast rule, but if you're new to VBA or not using it often, it's simpler to use formulas).
Using those ideas, I came up with this:
With Sheet1
.Range(.Cells(2, 8), .Cells(lastRow, 8)).FormulaR1C1 = _
"=IF(AND(RC[-5]=R[1]C[-5],RC[-5]=R[-1]C[-5],RC[-1],NOT(R[-1]C[-1]),R[1]C[-1]),""O""," & _
"IF(AND(RC[-5]=R[1]C[-5],RC[-5]<>R[-1]C[-5],RC[-1]),""FromYes""," & _
"IF(AND(RC[-5]=R[1]C[-5],RC[-5]=R[-1]C[-5],RC[-1],R[-1]C[-1],NOT(R[1]C[-1])),""D""," & _
"IF(AND(RC[-5]<>R[1]C[-5],RC[-5]=R[-1]C[-5],RC[-1]),""ToTmr"",""""))))"
End With
This is setting the formula for the range of column H, rows 2 through the lastRow, for Sheet1 equal to your formula. The removes the need to loop rows, making it much quicker.
I always find it is easier to work with R1C1 format when entering formulas through VBA. It's easy enough to get that format; you just need to go the formula options in Excel and select R1C1 Reference Style. Then you can copy it into the VBA and escape quotation marks with another quotation mark.
Using this, you can easily write a tool that loops through the files, finds the last row on the appropriate sheet, and sets the formula.
Related
I have a sheet that pulls columns of data from an external source, and a macro which then uses that data to calculate and populate other columns in the sheet. The calculation outputs on each row depend on the values of the row above. Ultimately, I would like to set the value of one column in the very last row to zero, by changing the input data in one of the first cells. If this were all Excel formulas then I could do a simple Goal Seek, but all of the calculations are carried out in vba.
I am new to vba so I'm not sure if what I'm asking is even possible. Here is an example of how my macro for the calculations looks:
Public Sub Calculate()
i = 2
Do While Cells(i, 3) <> ""
Cells(i, 31).Value = Cells(i, 30) / (1 + Cells(i, 61))
Cells(i, 28).Value = -Cells(i, 14) * Cells(i, 31)
Cells(i, 27).Value = (-Cells(i, 26) * Cells(i, 2))
Cells(i, 32).Value = Cells(i, 15) + Cells(i, 2) + Cells(i,27)
Cells(i, 29).Value = Cells(i, 61) * (Cells(i, 15) + Cells(i, 2)
Cells(i, 61).Value = (Cells(i, 60) + 1)
Cells(i, 62).Value = (Cells(18, 3) + 1)
Cells(i + 1, 15).Value = Cells(i, 32)
Cells(i + 1, 16).Value = Cells(i, 33)
i = i + 1
Loop
End Sub
Basically, the value of the last row of column 32 (AF) depends on the value of the first row of column 2 (B). So I would like to vary the value of B2 to make AF500 = 0, based on these calculations. I would love to avoid having formulas in the sheet so I hope this is possible. Thank you for taking the time to read, and sorry for the awful code.
I want to automatically update sheet 2 & sheet 3 from sheet 1 as per data. It will be helpful if it gets resolved.
I am new to visual basic coding so unaware of its script but aware of c++. Whatever the solution may be it will be helpful.
Screen shot of the three sheets - I was unable to upload the Excel files
I have tried the following code:
Sub FindMatches()
Dim oldrow As Integer
Dim newrow As Integer
For oldrow = 4 To 14
For newrow = 3 To 20
If Cells(oldrow, 12) = Cells(1, newrow) And Cells(oldrow, 13) = Cells(newrow, 1) Then 'date and brand
If Cells(1, 14) = Cells(newrow, 2) Then
Cells(newrow, 3).Value = Cells(oldrow, 14).Value ' m1
End If
If Cells(1, 15) = Cells(newrow + 1, 2) Then
Cells(newrow + 1, 3).Value = Cells(oldrow, 15).Value ' m2
End If
If Cells(1, 16) = Cells(newrow + 2, 2) Then
Cells(newrow + 2, 3).Value = Cells(oldrow, 16).Value ' m3
End If
If Cells(1, 17) = Cells(newrow + 3, 2) Then
Cells(newrow + 3, 3).Value = Cells(oldrow, 17).Value ' issue
End If
If Cells(1, 18) = Cells(newrow + 4, 2) Then
Cells(newrow + 4, 3).Value = Cells(oldrow, 18).Value ' repack
End If
If Cells(1, 19) = Cells(newrow + 5, 2) Then
Cells(newrow + 5, 3).Value = Cells(oldrow, 19).Value ' extra
End If
If Cells(1, 20) = Cells(newrow + 6, 2) Then
Cells(newrow + 6, 3).Value = Cells(oldrow, 20).Value ' wastage
End If
End If
Next newrow
Next oldrow
End Sub
Maybe you even don't need VBA to update the data. You can simply enter a formula =C2 into a cell to reference (and retrieve the data from) a cell.
You also can reference cells from other sheets like this =Sheet1!C2.
And you can use Functions such as IF to do more complex cases and logic.
You just cant use Cells without sheet referencing. Because excel dont know which sheet you wana to use and assuming active sheet. So you need to you ActiveSheet.cells() and switching active sheet (but its not highly recommended). Instead use sheet declaration like this
Dim myLovelySheet as worksheet
Set mylovelySheet = Sheets("yourCuteSheetname")
and then you can work with sheet like with object (you will be familiar to it from C++)
myLovelySheet.cells()
or you can perform multiple operations on sheet with construction like this
with myLovelySheet
.cells()
.cells()
'etc
end with
Basicaly your approach is almost correct, but try study more code. I can recommend this which have multiple examples with good practise
I have two rows of data, fracture pressure and depth. I have to code in vba to generate the polynomial (quadratic for this case) equation and then output the coefficients to the worksheet. I am using Linest and Index. For this two rows of data, I don't know how many datasets I have because I need to delete some noisy data first (the definition of noisy data is randomly so the number of datasets vary each time), so I can't use something like "A17:A80" in the linest function. However, it looks like the worksheet function in vba can't work for arrays.
Dim Frac_x, Frac_y As Range
Dim X
Set Frac_x = Range(Cells(17, 1), Cells(e - 1, 1))
Set Frac_y = Range(Cells(17, 7), Cells(e - 1, 7))
X= Application.WorksheetFunction.LinEst(Frac_y,Frac_x,{1,2})
Cells(3, 8).Value = Application.WorksheetFunction.Index(X, 1, 1)
Cells(4, 8).Value = Application.WorksheetFunction.Index(X, 1, 2)
Cells(5, 8).Value = Application.WorksheetFunction.Index(X, 1, 3)
In this code, e is defined in the previous code, (e-1) represents the total number of datasets. However, I keep getting { is a invalid character for the line: X= Application.WorksheetFunction.LinEst(Frac_y,Frac_x,{1,2})
Then I did some researches and modified the code to:
Dim Frac_x, Frac_y As Range
Dim X
Set Frac_x = Range(Cells(17, 1), Cells(e - 1, 1))
Set Frac_y = Range(Cells(17, 7), Cells(e - 1, 7))
X = Application.Evaluate("=linest(" & Frac_y & "," & Frac_x & "^ {1,2}))")
Cells(3, 8).Value = Application.WorksheetFunction.Index(X, 1, 1)
Cells(4, 8).Value = Application.WorksheetFunction.Index(X, 1, 2)
Cells(5, 8).Value = Application.WorksheetFunction.Index(X, 1, 3)
Then I keep getting Type Dismatch error for the line:
X = Application.Evaluate("=linest(" & Frac_y & "," & Frac_x & "^ {1,2}))")
I am sure the two ranges frac_y and frac_x their type matches. Anyone could help?
You are right, that Excel VBA can't do things like arrVariable^{1,2}. That must be done with loops over the array items.
But the Evaluate approach should work. But your formula string is not correct. To detect and avoid such incorrectness, I will ever concatenate such formula strings within a String variable first. Then I can simply check the variable's value.
Example, Values are in A17:A26 and G17:G26:
Sub test()
Dim Frac_x As Range, Frac_y As Range
Dim X
e = 27
With ActiveSheet
Set Frac_x = .Range(.Cells(17, 1), .Cells(e - 1, 1))
Set Frac_y = .Range(.Cells(17, 7), .Cells(e - 1, 7))
arrX = Frac_x
ReDim arrX2(1 To UBound(arrX), 1 To 2) As Double
For i = LBound(arrX) To UBound(arrX)
arrX2(i, 1) = arrX(i, 1)
arrX2(i, 2) = arrX(i, 1) * arrX(i, 1)
Next
X = Application.LinEst(Frac_y, arrX2)
'sFormula = "=LINEST(" & Frac_y.Address & "," & Frac_x.Address & "^{1,2})"
'X = Application.Evaluate(sFormula)
.Range(.Cells(3, 8), .Cells(5, 8)).Value = Application.Transpose(X)
End With
End Sub
Hints: Use Application.LinEst instead of Application.WorksheetFunction.LinEst. The latter will throw an error if the function cannot work while the first will return an error value instead. So the first will not interrupt the program as the latter will do.
I received many Excel files from a client.
Their system extracted the data into a spreadsheet, but one column is having issues. If the text was too long, it would put the remaining text into the cell below it.
This causes all the other fields in that row to be blank, except for the overflow.
How can I merge cells at issue into one for all files I received?
I uploaded a screen shot of the file as an example. Notice on row 8 that H8 is the only cell. That needs to be merged with H7. Not every row is at issue though.
asuming A is the main (and empty for doubles)
asuming H holds the text
then in L1 and copy down
=H1&IF(LEN(A2),H2,"")
simplest way (then copy values from L to H and delete empty lines (simply with filter)
when having unknown number of lines (after splitting) you better use vba (or simply repeat the whole procedure till there no empty lines anymore...
doing it in VBA:
Sub testing()
Dim i As Long
While Len(Cells(i + 1, 8))
i = i + 1
While Len(Cells(i + 1, 1)) = 0 And Len(Cells(i + 1, 8))
Cells(i, 8) = Cells(i, 8) & Cells(i + 1, 8)
Rows(i + 1).Delete
Wend
Wend
End Sub
most programs skip spaces so you may want to use:
=H1&IF(LEN(A2)," "&H2,"")
or for vba change Cells(i, 8) = Cells(i, 8) & Cells(i + 1, 8) to Cells(i, 8) = Cells(i, 8) & " " & Cells(i + 1, 8)
This will concatenate the texts in H and delete the row that is not useful :
Sub test_bm11()
Dim wS As Worksheet, _
LastRow As Long, _
i As Long
Set wS = ActiveSheet
With wS
LastRow = .Range("H" & .Rows.Count).End(xlUp).Row
For i = LastRow To 2 Step -1
If .Cells(i, "A") <> vbNullString Then
Else
.Cells(i, "H").Offset(-1, 0) = .Cells(i, "H").Offset(-1, 0) & .Cells(i, "H")
.Cells(i, "H").EntireRow.Delete
End If
Next i
End With
End Sub
I cannot seem to solve this VBA riddle I've been working on, please help. I'm new at this and I'm probably over complicating it
Essentially, there are two worksheets - one titled Master and the other will be created fresh daily by date. The Master tab contains 10000 rows of historical data filled from Columns A:X. The other tab generally has about 300 rows of fresh data and also contains like Columns A:X, only with blank cells in Columns A:B. I'm trying to find matches with the master tab, and if so, populate the corresponding results in cells A and B from the master to the daily. If nothing, leave blank. It is crucial that Cells H:M and R:W are identical matches.
Below is my crazy attempt, Thank you in advance for helping
Sub Previous()
Dim u As Long
u = 2
Do While ActiveSheet.Cells(u, 6) <> ""
Dim i As Long
i = 2
Do While Worksheets("Master").Cells(i, 6) <> ""
If ActiveSheet.Range(Cells(u, 8), Cells(u, 13)) _
= Worksheets("Master").Range(Cells(i, 8), Cells(i, 13)) _
And ActiveSheet.Range(Cells(u, 18), Cells(u, 23)) _
= Worksheets("Master").Range(Cells(i, 18), Cells(i, 23)) _
And ActiveSheet.Cells(u, 2) = "" Then
ActiveSheet.Range(Cells(u, 1), Cells(u, 2)) _
= Worksheets("Master").Range(Cells(i, 1), Cells(i, 2))
Else: i = i + 1
End If
Loop
u = u + 1
i = 2
Loop
End Sub
First of all, I don't believe this snippet does what you think it does.
Worksheets("Master").Range(Cells(i, 8), Cells(i, 13))
In that snippet Cells(i,8) references the ActiveSheet, not Sheets("Master").
There is a note on this about halfway down the page on msdn's Range Object documentation.
You can simplify your code a great deal by assigning some worksheet variables.
dim actWs as Worksheet
dim mstWs as Worksheet
Set actWs = Activesheet
Set mstWs = Sheets("Master")
'then reference your ranges like this
mstWs.Cells(i,8)
But, that's not what is causing your runtime error.
Simply put, you can not compare ranges that way. You need to check the value of each cell, so you end up with another layer of nested loops.
dim u as long ' active sheet row counter
dim i as long ' master sheet row counter
dim c as long ' column counter
For u = 2 to actWs.Range("A" & .Rows.Count).End(xlUp).Row 'find last row in column "A" of active sheet
For i = 2 to mstWs.Range("A" & .Rows.Count).End(xlUp).Row 'find last row in column "A" of master sheet
For c = 8 to 13
If actWs.Cells(i,c) = mstWs.Cells(i,c) Then
'Do stuff
End if
next c 'next column
next i 'next master sheet row
next u 'next active sheet row
This is obviously a simplified version of what you'll need to do. Be careful of line continuations (" _ ") and code indentation. It's easy to trick yourself into thinking your program should flow in a way that it isn't. It would be advisable to store the value's you're checking for equality in variables to make it easier to read. You might more readily notice where you're going wrong.
Sub Previous()
Dim actWs As Worksheet
Set actWs = ActiveSheet
Dim mstWs As Worksheet
Set mstWs = Sheets("Master")
Dim u As Long
Dim i As Long
u = 2
Do While actWs.Cells(u, 6) <> ""
For i = 2 To mstWs.Range("C" & Rows.Count).End(xlUp).Row
If actWs.Cells(u, 8) = mstWs.Cells(i, 8) And actWs.Cells(u, 9) = mstWs.Cells(i, 9) And actWs.Cells(u, 10) = mstWs.Cells(i, 10) And actWs.Cells(u, 11) = mstWs.Cells(i, 11) And actWs.Cells(u, 12) = mstWs.Cells(i, 12) And actWs.Cells(u, 13) = mstWs.Cells(i, 13) And actWs.Cells(u, 18) = mstWs.Cells(i, 18) And actWs.Cells(u, 19) = mstWs.Cells(i, 19) And actWs.Cells(u, 20) = mstWs.Cells(i, 20) And actWs.Cells(u, 21) = mstWs.Cells(i, 21) And actWs.Cells(u, 22) = mstWs.Cells(i, 22) And actWs.Cells(u, 23) = mstWs.Cells(i, 23) Then
mstWs.Select
Range(Cells(i, 1), Cells(i, 2)).Select
Selection.Copy
actWs.Select
Range(Cells(u, 1), Cells(u, 2)).Select
actWs.Paste
End If
Next i
u = u + 1
Loop
End Sub