Enter User Defined Function into Column Using VBA - excel

I currently am using a formula in Column J of Sheet 2 of my workbook that will look up values from 5 columns on Sheet 1 and return the corresponding text. For example if the value from column M on Sheet 2 matches any of the values from column J on Sheet 1 it would return "N", if not it would look in column K and if matched anything there it would return D, and so on. I am doing this in VBA so the formula used is
ActiveSheet.Range("J2:J" & intLastRow).FormulaR1C1 = _
"=IFERROR(IF(ISNUMBER(MATCH(RC[3],Sheet1!C10,0)),""N"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C11,0)),""D"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C12,0)),""R"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C13,0)),""G"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C14,0)),""F"",""""))))), """")"
This formula works well and fills in the corresponding values. I then created a user defined function that will look up all of the values in column J that are associated with an ID number found in column C and separate them by commas. This function also works well when entered into a cell.
Function get_areas(ID As String) As String
Dim rng As Range, cel As Range
Set rng = Range("A2:A" & Cells(rows.count,1).End(xlUp).Row)
Dim areas As String
For Each cel In rng
If IsNumeric(Left(cel, 1)) And cel.Offset(0, 2) = ID Then
If InStr(1, areas, cel.Offset(0, 9)) = 0 Then
areas = cel.Offset(0, 9) & ", " & areas
End If
End If
Next cel
areas = Trim(Left(areas, Len(areas) - 2))
get_areas = areas
End Function
Ideally, what I would like to do is run the original formula in all cells in column J that DON'T start with Master in Column A and then run the get_areas($C2) function in all cells that DO start with master in Column A. If that is not feasible, then I would like to run the get_areas function in all cells that are blank (meaning they didn't return anything from the original formula, but still have the formula in them) in VBA. I have tried modifying the original formula to read
ActiveSheet.Range("J2:J" & intLastRow).FormulaR1C1 =
"=IFERROR(IF(LEFT(RC[-9],6)=""master"", get_areas(RC[-7]),
IF(ISNUMBER(MATCH(RC[3],Sheet1!C10,0)),""N"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C11,0)),""D"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C12,0)),""R"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C13,0)),""G"",
IF(ISNUMBER(MATCH(RC[3],Sheet1!C14,0)),""F"","""")))))), """")"
but received errors about the get_areas function.

WorksheetFunction.Trim
All of this might have nothing to do with your case but might be useful in some similar cases. It just keeps ringing in my head and you know how it is when you can't keep your mouth shut.
I would have written the function like this:
Function get_areas(ID As String) As String
Dim rng As Range
Dim i As Long
Dim areas As String
Set rng = Range("A2:A" & Cells(Rows.Count, 1).End(xlUp).Row)
With rng
For i = 1 To .Cells.Count
If IsNumeric(Left(.Cells(i, 1))) And .Cells(i, 1).Offset(0, 2) = ID Then
If InStr(1, areas, .Cells(i, 1).Offset(0, 9)) = 0 Then
If i > 1 Then
areas = areas & ", " & .Cells(i, 1).Offset(0, 9)
Else
areas = .Cells(i, 1).Offset(0, 9)
End If
End If
End If
Next
End With
get_areas = WorksheetFunction.Trim(areas)
End Function
which in all is not so important as the 'WorksheetFunction' part.
WorksheetFunction.Trim removes all spaces except single spaces between words, while VBA's Trim function only removes the left and right spaces.
The other most noticeable difference is the 'If i > 1' block.

Related

How to use activecell row and column in VBA function with filldown?

I wrote a function which will concatenate all the cells to the left of the cell the function is in, using a delimiter. My code is:
Public Function Concat_To_Left(delim As String)
Dim C, R As Long
Dim S As String
Dim Cell As Range
Set Cell = ActiveCell
C = Cell.Column
R = Cell.Row
S = Cells(R, 1).Value
For i = 2 To (C - 1)
S = S & delim & Cells(R, i).Value
Next i
Concat_To_Left = S
End Function
This code works if calculating a single row. The problem I'm running into is that the cell.row and cell.column seem to be saved from the first cell when I fill the function to the bottom of a column (by double clicking the bottom right of the cell in the excel sheet). This results in all cells with this function having the same value as the cell being filled down from.
Screen-Updating, Events, and Alerts are all on/true. Application.Calculation is set to xlCalculationAutomatic
Can anyone tell me how to make this function work on each cell the formula is filled down into, using the proper row and column for each cell (not that column matters when filling down)?
Scott's comment about using TEXT join worked as a workaround to what I was trying to accomplish.
=TEXTJOIN(", ",TRUE,B2:INDEX(2:2,COLUMN()-1))
The link he provided to the custom code for TEXTJOIN was very nice as well:
MS Excel - Concat with a delimiter
Adding Application.Volatile did not make my function work. I did not find a way to get my function working with fill down without needing a range parameter, so TEXTJOIN is the next best option and answers my question for now.
EDIT:
I wrote this macro to work instead of a function:
Private Sub Concat_To_Left()
Dim C, R, LR As Long
Dim Cell As Range
LR = ActiveWorkbook.ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
C = ActiveCell.Column
R = ActiveCell.Row
For Each Cell In ActiveWorkbook.ActiveSheet.Range(Cells(R, C), Cells(LR, C))
Cell.Value = Cells(Cell.Row, 1).Value
For i = 2 To (C - 1)
Cell.Value = Cell.Value & "|" & Cells(Cell.Row, i).Value
Next i
Next Cell
End Sub
This one uses "|" as a delimiter, fills down from the active cell to lastrow concatenating every cell to the left, including blanks.

VBA Comma Separated Look-Up

I'm looking to either use a formula or VBA to lookup values in column J associated with the same ID number and put them into any starting with "Master" in Column A separated by commas.
In the example below, I would like cell J2 to return
"D, G, R, N" and if there ere any duplicates to only have them listed once. The values in column J are currently looking up values from another list using the formula :
=IFERROR(IF(ISNUMBER(MATCH(M2,Sheet1!$J:$J,0)),"N",
IF(ISNUMBER(MATCH(M2,Sheet1!$K:$K,0)),"D",
IF(ISNUMBER(MATCH(M2,Sheet1!$L:$L,0)),"R",
IF(ISNUMBER(MATCH(M2,Sheet1!$M:$M,0)),"G",
IF(ISNUMBER(MATCH(M2,Sheet1!$N:$N,0)),"F",""))))), "")
You can use this UDF. Since you're looking for a formula, put this in the blank cells in Column J.
=get_areas(C2)
Then add this in to a Workbook Module:
Function get_areas(ID As String) As String
Dim rng As Range, cel As Range
Set rng = Range("A2:A" & Cells(rows.count,1).End(xlUp).Row)
Dim areas As String
For Each cel In rng
If IsNumeric(Left(cel, 1)) And cel.Offset(0, 2) = ID Then
If InStr(1, areas, cel.Offset(0, 9)) = 0 Then
areas = cel.Offset(0, 9) & ", " & areas
End If
End If
Next cel
areas = Trim(Left(areas, Len(areas) - 2))
get_areas = areas
End Function

Attempting to Highlight Cells Based on String within Cell

Basically what I'm trying to do is the following:
First, determine if I'm in the correct row of cells based on the String value of the cell.
i.e If the current cell's string value contains the string AB1 or AB2, go through the entire row.
Once that has been determined, I would like to highlight the cells either green (if the cell holds a value greater than 5) or blue (if the cell holds a value between 4 and 5).
The above if block is not giving me trouble, it's the initial procedure.
What is stopping me from completing this is the run-time [error '91']: "Object variable or With block variable not set".
I have some programming experience, but no VBA experience. Any help would be greatly appreciated.
Sub ChangeCellColor()
Dim columnD As Range
Dim str1, str2 As String
Dim currCell As Range
Dim rightCell As Range
Dim i As Long
str1 = "AB1"
str2 = "AB2"
Columns(1).Font.Color = vbBlack
For i = 1 To Rows.Count
'If the current cell in the D column contains either the string AB1 or AB2, it will look into the values here.
If (currCell.Cells(i, 4).Value = str1) Or (currCell.Cells(i, 4).Value = str2) Then
'From the cell range of
For j = 1 To Range("E10").End(xlToRight)
If rightCell.Cells(j, 5) >= 5# Then
rightCell.Interior.Color = vbRed
ElseIf (rightCell.Cells(j, 5) >= 4 And rightCell.Cells(j, 5) <= 4.99) Then
cell.Interior.Color = vbYellow
End If
Next j
End If
Next i
End Sub
Try this: the following code looks at each cell in Column D, and checks the cell value to determine if cell.value = str1 or str2. Then, it loops through each cell in that row, beginning with column E, changing the color based on your parameters.
Also, try the Usedrange property of the Worksheet object to get the number of rows you need.
Sub ChangeCellColor()
Dim str1, str2 As String
Dim i As Integer
Dim j As Integer
Dim col As Integer
str1 = "AB1"
str2 = "AB2"
Columns(1).Font.Color = vbBlack
For i = 1 To ThisWorksheet.Usedrange.Rows.Count
With ThisWorksheet
'If the current cell in the D column contains either the string AB1 or AB2, it will look into the values here.
If .Cells(i, 4).Value = str1 Or .Cells(i, 4).Value = str2 Then
col = .Range("D" & i).End(xltoRight).Column
For j = 5 To col
If .Cells(i, j).Value >= 5 Then
.Cells(i,j).Interior.Color = vbRed
Else
If .Cells(i, j).Value >= 4 And .Cells(i, j).Value <= 4.99 Then
.Cells(i,j).Interior.Color = vbYellow
End If
End If
Next j
End If
End With
Next i
End Sub
There are a several issues...First, the error is because assigning to a Range variable requires the Set keyword, like so:
Set columnD = Range("D:D")
Second, in your For loop, you're comparing an integer to a range. If you want to loop through to the right-most column, you can do this:
For j = 1 to Range("E10").End(xlToRight).Column
Third, it looks like you've intended to use i for rows and j for columns? If so, you've got your js in the wrong place.
Assuming that is are rows and js are columns, I believe that as you're checking your cells for values, you should be referencing Cells(i, j,), (making both column and row selection dynamic) rather than hardcoding the value of 5.
Finally, you actually don't need those three range variables that you declared at the beginning at all. There's no need to position the cell inside an existing range (though you can if you wish). VBA assumes that you're dealing with the active sheet of the active workbook. As long as those two assumptions hold, then Cells(i,j) works just fine. If you want to add some specificity/protect from running on the wrong sheet, you can use Sheets("Sheet1").Cells(i,j).
PS--I assume that the '#' after the 5 is a typo?
You might want to either assign range value to currCell and rightCell or get rid of it.
Sub ChangeCellColor()
Dim columnD As Range
Dim str1, str2 As String
Dim currCell As Range
Dim rightCell As Range
Dim i As Long
str1 = "AB1"
str2 = "AB2"
Columns(1).Font.Color = vbBlack
For i = 1 To Rows.Count
'If the current cell in the D column contains either the string AB1 or AB2, it will look into the values here.
If (Cells(i, 4).Value = str1) Or (Cells(i, 4).Value = str2) Then
'From the cell range of
For j = 1 To Range("E10").End(xlToRight)
If Cells(j, 5) >= 5 Then
Cells(j, 5).Interior.Color = vbRed
ElseIf (Cells(j, 5) >= 4 And Cells(j, 5) <= 4.99) Then
Cells(j, 5).Interior.Color = vbYellow
End If
Next j
End If
Next i
End Sub
I think possible without VBA but with Conditional Formatting and, for green, a formula rule such as:
=AND(OR(NOT(ISERROR(FIND("AB1",$D1))),NOT(ISERROR(FIND("AB2",$D1)))),N(A1)>5)

How to run an equation along whole column in excel vba

I want to run an excel vba which will go down column E and upon finding the value = "capa" will go two cell below, calculate the hex2dec value of that cell, present it by the cell with the value "capa" in column F and continue to search down column E.
So far I've came with the below but it doesn't work:
For Each cell In Range("E:E")
If cell.Value = "Capa" Then
ActiveCell.Offset.FormulaR1C1 = "=HEX2DEC(R[2]C[-1])"
End If
Next cell
Thanks!
How about something like this?
This will search volumn E for "Capa" and, if found, will place formula in column F using the value directly below "Capa" in column E
Sub CapaSearch()
Dim cl As Range
For Each cl In Range("E:E")
If cl.Value = "Capa" Then
cl.Offset(0, 1).Formula = "=HEX2DEC(" & cl.Offset(1, 0) & ")"
End If
Next cl
End Sub
You really want to limit the loop so you don't loop over the whole sheet (1,000,000+ rows in Excel 2007+)
Also, copying the source data to a variant array will speed things up too.
Try this
Sub Demo()
Dim dat As Variant
Dim i As Long
With ActiveSheet.UsedRange
dat = .Value
For i = 1 To UBound(dat, 1)
If dat(i, 6 - .Column) = "Capa" Then
.Cells(i, 7 - .Column).FormulaR1C1 = "=HEX2DEC(R[2]C[-1])"
End If
Next
End With
End Sub

Macro to move number with dash to new cell

In Excel, I am trying to get a macro to move numbers with a "-".
I have a column E with a list of numbers
54525841-1
454152
1365466
1254566-1
1452577-1
I want a macro to move all the numbers that have a dash or hyphen at the end to column C.
So I would need E1 54525841-1 to be moved to C1.
You'll need to change "Sheet1" to the name of the sheet where your data is.
This looks through every cell (with data) in the E column and moves the value accross to the C column if it contains a dash.
Sub MoveDashes()
Dim Sheet As Worksheet
Dim Index As Long
Set Sheet = ThisWorkbook.Worksheets("Sheet1")
For Index = 1 To Sheet.Cells(Application.Rows.Count, "E").End(xlUp).Row
If InStr(1, Sheet.Cells(Index, "E"), "-") > 0 Then
Sheet.Cells(Index, "C") = Sheet.Cells(Index, "E").Value
Sheet.Cells(Index, "E").Value = ""
End If
Next
End Sub
Does it have to be a macro? How about Advanced Filter?
Your numbers are in column E. Let's assume they have a header.
E1: Number
E2: 54525841-1
E3: 454152
E4: 1365466
E5: 1254566-1
E6: 1452577-1
In a separate area of your worksheet (let's say column G) put the following criteria:
G1: Number
G2: *-*
Your advanced filter criteria would look like this:
Anything with a "-" in it will be copied to column C.
I got it to work by this:
Sub MoveDash()
x = Range("E" & Rows.Count).End(xlUp).Row
For Each Cell In Range("E2:E" & x)
If InStr(Cell, "-") <> 0 Then
Cell.Offset(, 1) = Cell
Cell.ClearContents
End If
Next Cell
end sub
You can do this without VBA, but here is an efficient way to do it using the dictionary object.
Sub MoveNumbersWithDash()
Application.ScreenUpdating = False
Dim i As Long, lastRow As Long
Dim varray As Variant
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
lastRow = Range("E" & Rows.Count).End(xlUp).Row
varray = Range("E1:E" & lastRow).Value
For i = 1 To UBound(varray, 1)
If InStr(1, varray(i, 1), "-") <> 0 Then
dict.Add i, varray(i, 1)
End If
Next
Range("C1").Resize(dict.Count).Value = _
Application.WorksheetFunction.Transpose(dict.items)
Application.ScreenUpdating = True
End Sub
How it works:
The major theme here is avoiding calls to Excel (like a for each loop). This will make the function blazing fast (especially if you have tens and thousands of rows) and more efficient. First I locate the last cell used in E then dump the entire row into a variant array in one move. Then I loop through each element, checking if it contains a "-", if it does, I add it to a dictionary object. POINT: Add the entry as the ITEM, not KEY. This makes sure that we allow for duplicates. The variable I will be unique for each entry, so I use that as the key. Then I simple dump the entire array of cells with "-" into column C.
Why Dictionary?
The dictionary object is very fast and comes with 2 really great functions: .Keys and .Items. These will return an array of all the keys or items in the dictionary, which you can use the Transpose function on to dump an entire column of values into Excel in one step. Super efficient.

Resources