I'm new to VBA and I can't manage to do what I want although it's very simple.
I need to automatically modify cells of a big (333x333) empty (full of zeros) spreadsheet.
In a separate spreadsheet I have the row and column of all the cells to modify. (5000 of them)
A for loop seems to be suited for this purpose.
Here is the code of my macro. The problem appears on the line before the last one.
Dim val1 As String, val2 As String, i As Integer
For i = 1 To 333
Sheets("Feuil2").Activate
ActiveSheet.Cells(i, 1).Select
val1 = Cells(i, 1).Value
val2 = Cells(i, 2).Value
Sheets("Classeur2.csv").Select
Cells(val1, val2).Select
ActiveCell.FormulaR1C1 = "1"
Next i
The line that causes a problem is this one : Cells(val1, val2).Select
I believe my error is a syntax error. But I can't find out what I should add before, after or around my two variables "val1" and "val2"
What do you think ?
Thanks a lot for your help.
Nicolas.
Edit
My problem is now solved :
The first answer is exactly what I needed to male my macro work.
The second answer is the proper and faster way to do it.
No need to activate or selection sheets or cells if you're using VBA. You can access it all directly.
The code:
Dim rng As Range
For Each rng In Sheets("Feuil2").Range("A1:A333")
Sheets("Classeur2.csv").Cells(rng.Value, rng.Offset(, 1).Value) = "1"
Next rng
is producing the same result as Joe's code.
If you need to switch sheets for some reasons, use Application.ScreenUpdating = False at the beginning of your macro (and Application.ScreenUpdating=True at the end). This will remove the screenflickering - and speed up the execution.
VAL1 and VAL2 need to be dimmed as integer, not as string, to be used as an argument for Cells, which takes integers, not strings, as arguments.
Dim val1 As Integer, val2 As Integer, i As Integer
For i = 1 To 333
Sheets("Feuil2").Activate
ActiveSheet.Cells(i, 1).Select
val1 = Cells(i, 1).Value
val2 = Cells(i, 2).Value
Sheets("Classeur2.csv").Select
Cells(val1, val2).Select
ActiveCell.FormulaR1C1 = "1"
Next i
Related
There seems to be lots of similar questions but nothing that quite seems to answer this one.
I have created the below sub to convert a column of text to dates. It works in some scenarios, i.e. where the date has 8 characters such as 10022017, but not if the value has 7 characters such as 1022017.
Any ideas appreciated.
Sub convertDate()
'Find the last Row with data in a Column
Dim lastRow As Long
Dim i As Long
With ActiveSheet
lastRow = .Cells(.Rows.Count, ColumnSelect).End(xlUp).Row
End With
'convert column from string to date
For i = RowSelect To lastRow
Cells(i, ColumnSelect).Value = Format(Cells(i, ColumnSelect).Value, "00/00/0000")
Next i
End Sub
Hi again all, still having a few problems, the ideal formula seems to be =DATEVALUE(TEXT(A1,"00-00-0000")) if for example A1 = e.g. 02022017. Still haven't found the best way to do this with VBA as everything seems to run up against the truncated zeros problem. Thanks in advance
If your format is going to be consistent then try this.
This adds a 0 before Cells(i, ColumnSelect).Value when there are 7 characters.
For i = RowSelect To lastRow
Select Case Len(Trim(Cells(i, ColumnSelect).Value))
Case 8
Cells(i, ColumnSelect).Value = Format(Cells(i, ColumnSelect).Value, "00/00/0000")
Case 7
Cells(i, ColumnSelect).Value = Format("0" & _
Cells(i, ColumnSelect).Value, "00/00/0000")
End Select
Next i
But be careful with numbers like 1122017... this can be 11/2/2017 or 1/12/2017
Edit
Looking at 10022017, I realised that 1122017 will be 1/12/2017 and not 11/2/2017 as your mm zeros are not getting truncated.
OP wants a formula as well
=IF(LEN(A1)=8,TEXT(DATEVALUE(LEFT(A1,2)&"/"&MID(A1,3,2)&"/"&RIGHT(A1,4)),"dd/mm/yyyy"),TEXT(DATEVALUE(LEFT(A1,1)&"/"&MID(A1,2,2)&"/"&RIGHT(A1,4)),"dd/mm/yyyy"))
I have a macro that inserts 2 columns on my current sheet and pastes information from another sheet.
I want to create 2 variables that are assigned to each column that would change the next time I run the macro to paste the information in the next two columns.
Columns("BO:BO").Select
Selection.Insert Shift:=xlToRight
Range("BO2").Select
ActiveCell.FormulaR1C1 = "Feb weekly-wk 2"
Range("BO19").Select
ActiveCell.FormulaR1C1 = _
"=VLOOKUP(Comparison!RC2,'Jan16 wk4'!R3C15:R34C24,9,FALSE)"
Range("BO19").Select
Selection.AutoFill Destination:=Range("BO19:BO47"), Type:=xlFillDefault
Range("BO19:BO47").Select
Columns("BP:BP").Select
Selection.Insert Shift:=xlToRight
Range("BP2").Select
Selection.Style = "20% - Accent6"
Range("BP2").Select
ActiveCell.FormulaR1C1 = "Diff"
Range("BP19").Select
ActiveCell.FormulaR1C1 = "=RC[-2]-RC[-1]"
My idea is to set up a variable that I would replace my current "BO" and "BP" code with.
Dim X as String, Y as String
X = "BO"
y = "BP"
When I run the macro it would change the variable for this example "BO" to "BQ" and "BP" to "BR". Next time I run the macro would change the "BQ" to "BS" and "BR" to "BT".
I just cleaned your code a little:
Dim ColBO As Integer
Dim ColBP As Integer
Dim StrBO As String
Dim StrBP As String
StrBO = "BO"
StrBP = "BP"
ColBO = ActiveWorkbook.Range(StrBO & 1).Column 'instead of StrBO you could directly write ("BO" & 1)
ColBP = ActiveWorkbook.Range(StrBP & 1).Column 'Then you wouldnt need these two variables
Columns(ColBO).Insert Shift:=xlToRight
'Columns(ColBO).Select ' Trying to avoid selection but not sure if this works here...
'Selection.Insert Shift:=xlToRight
Range(1, ColBO).FormulaR1C1 = "Feb weekly-wk 2"
Range(19, ColBO).FormulaR1C1 = "=VLOOKUP(Comparison!RC2,'Jan16 wk4'!R3C15:R34C24,9,FALSE)"
Range(19, ColBO).AutoFill Destination:=Range("BO19:BO47"), Type:=xlFillDefault
Columns(ColBP).Insert Shift:=xlToRight 'Same here as above
Range(2, ColBP).Style = "20% - Accent6"
Range(2, ColBP).FormulaR1C1 = "Diff"
Range(19, ColBP).FormulaR1C1 = "=RC[-2]-RC[-1]"
For the future: If you can, try to avoid .Select/Selection/.Activate if possible. The code can mostly run without such commands and without activating a cell. ;)
If you are not actually writing BO/BP to the range you are transforming I would go with two ints, stored in a hidden sheet. Read/write each time you run the macro.
This is, in my opinion, the easier solution, other places to go would be global variables or storing it to a file.
If you want to use numeric variables you can change approach and use Cells instead of Range:
'You can use the rows below to know the column number
Range("BO1").Activate
ActiveCell.Value = ActiveCell.Column 'This way you get the column number into the cell
ColNum = ActiveCell.Column 'This way you get the column number into the variable
'So now you know that BO column number is 67 and you can use
Cells(1, 67) = "OK"
'Or, using variables:
RowNum = 1
ColNum = 67
Cells(RowNum, ColNum) = "You Got It!"
This makes you able to loop columns simply using a for ... next
If you need to loop from BO to BR you can use
For ColNum = 67 To 70
Cells(1, ColNum) = "OK"
Next ColNum
Hope it helps.
Please be aware that I am working with a series of ~1000 line medical information databases. Due to the size of the databases, manual manipulation of the data is too time consuming. As such, I have attempted to learn VBA and code an Excel 2010 macro using VBA to help me accomplish parsing certain data. The desired output is to split certain characters from a provided string on each line of the database as follows:
99204 - OFFICE/OUTPATIENT VISIT, NEW
will need to be split into
Active Row Active Column = 99204 ActiveRow Active Column+3 = OFFICE/OUTPATIENT VISIT, NEW
I have researched this topic using Walkenbach's "Excel 2013: Power Programming with VBA" and a fair amount of web resources, including this awesome site, but have been unable to develop a fully-workable solution using VBA in Excel. The code for my current macro is:
Sub EasySplit()
Dim text As String
Dim a As Integer
Dim name As Variant
text = ActiveCell.Value
name = Split(text, "-", 2)
For a = 0 To 1
Cells(1, a + 3).Value = Trim(name(a))
Next a
End Sub
The code uses the "-" character as a delimiter to split the input string into two substrings (I have limited the output strings to 2, as there exists in some input strings multiple "-" characters). I have trimmed the second string output to remove leading spaces.
The trouble that I am having is that the output is being presented at the top of the activesheet, instead of on the activerow.
Thank you in advance for any help. I have been working on this for 2 days and although I have made some progress, I feel that I have reached an impasse. I think that the issue is somewhere in the
Cells(1, a + 3).Value = Trim(name(a))
code, specifically with "Cells()".
Thank you Conrad Frix!
Yah.. funny enough. Just after I post I have a brainstorm.. and modify the code to read:
Sub EasySplit()
Dim text As String
Dim a As Integer
Dim name As Variant
text = ActiveCell.Value
name = Split(text, "-", 2)
For a = 0 To 1
ActiveCell.Offset(0, 3 + a).Value = Trim(name(a))
Next a
End Sub
Not quite the colkumn1,column4 output that I want (it outputs to column3,column4), but it will work for my purpose.
Now I need to incorporate a loop so that the code runs on each successive cell in the column (downwards, step 1) skipping all bolded cells, until it hits an empty cell.
Modified answer to modified request.
This will start on row 1 and continue until a blank cell is found in column A. If you would like to start on a different row, perhaps row 2 if you have headers, change the
i = 1
line to
i = 2
I added a check on the upper bound of our variant before doing the output writes, in case the macro is run again on already formatted cells. (Does nothing instead of erroring out)
Sub EasySplit()
Dim initialText As String
Dim i As Double
Dim name As Variant
i = 1
Do While Trim(Cells(i, 1)) <> ""
If Not Cells(i, 1).Font.Bold Then
initialText = Cells(i, 1).text
name = Split(initialText, "-", 2)
If Not UBound(name) < 1 Then
Cells(i, 1) = Trim(name(0))
Cells(i, 4) = Trim(name(1))
End If
End If
i = i + 1
Loop
End Sub
just add a variable to keep track of the active row and then use that in place of the constant 1.
e.g.
Dim iRow as Integer = ActiveCell.Row
For a = 0 To 1
Cells(iRow , a + 3).Value = Trim(name(a))
Next a
Alternate method utilizing TextToColumns. This code also avoids using a loop, making it more efficient and much faster. Comments have been added to assist with understanding the code.
EDIT: I have expanded the code to make it more versatile by using a temp worksheet. You can then output the two columns to wherever you'd like. As stated in your original question, the output is now to columns 1 and 4.
Sub tgr()
Const DataCol As String = "A" 'Change to the correct column letter
Const HeaderRow As Long = 1 'Change to be the correct header row
Dim rngOriginal As Range 'Use this variable to capture your original data
'Capture the original data, starting in Data column and the header row + 1
Set rngOriginal = Range(DataCol & HeaderRow + 1, Cells(Rows.Count, DataCol).End(xlUp))
If rngOriginal.Row < HeaderRow + 1 Then Exit Sub 'No data
'We will be using a temp worksheet, and to avoid a prompt when we delete the temp worksheet we turn off alerts
'We also turn off screenupdating to prevent "screen flickering"
Application.DisplayAlerts = False
Application.ScreenUpdating = False
'Move the original data to a temp worksheet to perform the split
'To avoid having leading/trailing spaces, replace all instances of " - " with simply "-"
'Lastly, move the split data to desired locations and remove the temp worksheet
With Sheets.Add.Range("A1").Resize(rngOriginal.Rows.Count)
.Value = rngOriginal.Value
.Replace " - ", "-"
.TextToColumns .Cells, xlDelimited, Other:=True, OtherChar:="-"
rngOriginal.Value = .Value
rngOriginal.Offset(, 3).Value = .Offset(, 1).Value
.Worksheet.Delete
End With
'Now that all operations have completed, turn alerts and screenupdating back on
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
You can do this in a single shot without looping using the VBA equivalent of entering this formula, then taking values only
as a formula
=IF(NOT(ISERROR(FIND("-",A1))),RIGHT(A1,LEN(A1)-FIND("-",A1)-1 ),A1)
code
Sub Quicker()
Dim rng1 As Range
Set rng1 = Range([a1], Cells(Rows.Count, "A").End(xlUp))
With rng1.Offset(0, 3)
.FormulaR1C1 = "=IF(NOT(ISERROR(FIND(""-"",RC[-3]))),RIGHT(RC[-3],LEN(RC[-3])-FIND(""-"",RC[-3])-1 ),RC[-3])"
.Value = .Value
End With
End Sub
I'm relatively new to VBA and I'm trying to write a macro that will compare two columns of data (first and last names). While traversing the column, any time first name = last name (ie. they're both blank or say UNKNOWN) I want the cell in the 9th column to be cleared and the cell in the 10th column to get the value UNKNOWN.
As of now, the code correctly recognizes any time when the first and last name are identical. My problem is that any time first name is a sub-string of any last name (ie. cell I2=David J2=Jones , I3=Joseph J3=Davidson) David gets compared with Davidson and is subsequently erased.
I've spent a while looking for similar problems and I haven't been able to adapt anything to my problem thus far. Thanks in advance for any help.
Sub compare_cols()
Dim Report As Worksheet
Dim i As Integer, j As Integer
Dim lastRow As Integer
Set Report = Excel.ActiveSheet
lastRow = Report.UsedRange.Rows.count
Application.ScreenUpdating = False
For i = 1 To lastRow ' This will find all identical pairs of cells in I,J (blank, blank) or (unknown, unknown). I stays blank, J gets UNKNOWN
For j = 1 To lastRow ' I think its currently erasing any matches (ex. if someones first name is James, it will get erased if there is a last name jameson)
If InStr(1, Report.Cells(j, 10).Value, Report.Cells(i, 9).Value, vbTextCompare) > 0 Then
Report.Cells(i, 9).Value = ""
Report.Cells(i, 10).Value = "UNKNOWN"
Exit For
Else
End If
Next j
Next i
Application.ScreenUpdating = True
End Sub
Unlike some other languages, you can compare strings in vba just using the "=" sign and that will find exact matches, which is what it appears you are looking for. Try
if Report.Cells(j, 10) = Report.Cells(i, 9) etc.
SO this started as me trying to help someone else, got stumped. So basically i have values in columns B, C, and D. if have my criteria in H2 and I2 and when my criteria in H2 and I2 matches in B and C then have the corresponding answer in D to populate J2. basically a vlookup with 2 criteria.
i have something like this.
Sub test()
Dim rngCrit1 As Range
Dim rngCrit2 As Range
Dim rngAnswer As Range
Dim strTarget As String
Set rngCrit1 = Range("H2")
Set rngCrit2 = Range("I2")
Set rngAnswer = Range("J2")
Range("B2").Select
strTarget = ActiveCell.Value
Do While strTarget <> ""
With ActiveCell
If strTarget = rngCrit1 Then
If .Offset(0, 1).Value = rngCrit2 Then
rngAnswer.Value = .Offset(0, 2)
Else
.Offset(1, 0).Select
strTarget = ActiveCell.Value
End If
End If
End With
Loop
End Sub
Now this thing just crashes, no debugging or anything. I am self taught so i'm sure i screwed the pooch here somewhere.
*Note this is just to satisfy my own interest not really important, so if it takes you more than 5 min please help someone else that needs it more than I.
Val1 Val2 Val3 Crit1 Crit2 Answer
a r 12 g v 22
b r 14
c s 15
d s 16
e t 18
f t 19
g y 20
g v 22
sample data
It's great that you're trying to improve your VBA skills. The first thing I'd suggest, which will improve any macro you write, is to avoid using .Select. Work directly with the range objects. For instance:
Range("B2").Select
strTarget = ActiveCell.Value
becomes
strTarget = Range("B2").Value
Also, in general, use vbNullString or Len(variable)=0 when checking for "empty" values instead of "". As for why your program is crashing, it may be your use of With. Like Select, it should be avoided in most cases (definitely in this one). Although you update ActiveCell, it's within the scope of the With statement, so once you close it (End With), those changes to ActiveCell are undone (I would suggest stepping through the macro and watch the values of strTarget and ActiveCell). This may not be the case, but I know it holds for other variables, which is why I avoid With (and avoid reassigning values in a With statement)
Anyway, I'd add the following code and rewrite the loop as follows:
Dim r as range
set r = Range("B2") 'keep in mind this range is on the ActiveSheet, so you're better
'off explicitly naming the Sheet e.g. Sheet1.Range("B2")
strTarget1 = Range("B2").Value
strTarget2 = Range("C2").Value
Do While Len(strTarget) <> 0
If strTarget1 = rngCrit1 Then
If strTarget2 = rngCrit2 Then
rngAnswer.Value = r.Offset(0,2)
Exit Do
End If
End If
set r = r.Offset(1,0)
strTarget1 = r.Value
strTarget2 = r.Offset(0,1).Value
Loop
Keep in mind you could also loop with a Long counter i for the row, then call Sheet1.Cells(i,1).Value, Sheet1.Cells(i,2).Value and so on for the values of the different columns of that row (instead of using a range object and .Offset
EDIT: After running your code, the reason for the crash is due to your If statements. You want to go to the next cell regardless. Remove the Else and put the End If statements before the Select. Add an Exit Do after your assignment statement in the 2nd If, since you want to stop looping if your two columns meet the criteria. I've updated my code to show this, as well.
INDEX and MATCH, or SUMPRODUCT tend to work well for this. An example of the former:
http://support.microsoft.com/kb/59482
if you can guarantee val1 and val2 will be unique (e.g. when searching for g & v, there is only 1 line with g and v) then you can use sumifs
I put val1,val2 and val3 in columns A,B, & C, and the search into E,F and the answer in G, and came up with this formula
=SUMIFS(C2:C9,A2:A9,E2,B2:B9,F2)
of course, this fails if val3 is not numeric, or there are more than 1 line with the letters you are looking for