Adding freshly created formula into new module - excel

I've just created a brand new macro. Took function down below from internet (all credits goes to trumpexcel.com), code down below
Function CONCATENATEMULTIPLE(Ref As Range, Separator As String) As String
Dim Cell As Range
Dim Result As String
For Each Cell In Ref
Result = Result & Cell.Value & Separator
Next Cell
CONCATENATEMULTIPLE = Left(Result, Len(Result) - 1)
End Function
Then I proceed to extract data from various columns and into the one (my table is 20 rows x 10 columns)
Sub conact_data()
Dim i As Integer
For i = 2 To Cells(Rows.Count, "A").End(xlUp).Row
Cells(i, "M").Value = Cells(i, "A").Value & " " & _
Cells(i, "B").Value & " / " & Cells(i, "D").Value & "; "
Next i
End Sub
Thanks to that I've got combined data from column A, B and D, so its 20 rows. All I want to do now is to concatenate data from M2:M21 using CONCATENATEMULTIPLE function therefore I try various approach (I want this huge line in P2 cell) like :
Cells(2, 16).Value = CONCATENATEMULTIPLE (M2:M21, " ")
or
Range("P2") = "CONCATENATEMULTIPLE (M2:M21, " ")"
I don't really know how to apply that
Secondly, I'd like withdraw the Cells(i, "B").Value as percentage. Can I do that in one line like Cells(i, "B").NumberFormat="0.00%".Value (which is not working for me obviously) else I need to copy column B into another column with number format and then combine the new column, properly formatted instead of column B?
Thanks in advance

Percent format: Range("B" & i).NumberFormat = "0.00%"
CONCATENATEMULTIPLE
In VBA, CHR(32) = " "
In Excel, CHAR(32) = " "
With that being said...
'Value
Range("P2").Value = CONCATENATEMULTIPLE(Range("M2:M21"), CHR(32))
'Formula
Range("P2").Formula = "=CONCATENATEMULTIPLE(M2:M21, CHAR(32))"
You should really qualify all of your ranges with a worksheet
Say your workbook has 10 sheets. When you say Range("P2"), how do we (VBE) know what sheet you mean? Objects need to be properly qualified. Sometimes this is not a huge issue, but when you are working across multiple sheets, not qualifying ranges can lead to some unexpected results.
You can qualify with a worksheet a few ways.
Directly: ThisWorkbook.Sheets("Sheet1").Range("P2").Copy
Or use a variable like so
Dim ws as Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
ws.Range("P2").Copy
Now there is no room for ambiguity (potential errors) as to the exact location of Range("P2")

First of all, remove your ConcatenateMultiple() code, and instead use Excel worksheet function CONCAT(), which takes a range and a delimiter as parameters.
Here is how you can handle the percentage issue and supply a default for non-numeric items. I've also cleaned up the way you reference your data range.
Sub concat_data()
Dim rngRow As Range, vResult As Variant
Const DEFAULT = 0 'Can also be set to a text value, eg. "Missing"
For Each rngRow In [A2].CurrentRegion.Rows
If IsNumeric(rngRow.Cells(, 4)) Then vResult = rngRow.Cells(, 4) * 100 & "%" Else vResult = DEFAULT
Range("M" & rngRow.Row) = rngRow.Cells(, 1) & rngRow.Cells(, 2) & "/" & vResult & ";"
Next
[M2].End(xlDown).Offset(1).Formula = "=CONCAT(M2:M" & [M2].End(xlDown).Row & ",TRUE,"" "")"
End Sub
I'm not a fan of hard-coding range references, like the [A2] or Range("M"), but will leave that for another time.

Related

Excel VBA Lookup two values in same row based on values in three columns

I have a table that looks like this:
Basically, "MatGroup" is a subtype of "StorLoc" which is a subtype of "Department".
I need to look for a match in those three columns, then return the values of two of the other columns to the right (those columns represent weeks).
I've highlighted an example in the picture above. There I'm searching for Department 1101, StorLoc 0001 and MatGroup 1225 in week 4 and 5, which should return the values 243 and 245, which can then be added together = 488.
For testing purpose, I have a working formula that does this in the same worksheet:
=SUMPRODUCT(XLOOKUP(K6&J8&K7;A2:A40&B2:B40&C2:C40;D2:E40))
However, I need a pure VBA solution for this as I'll be pulling this data into a completely separate workbook. I'm not able to use the same solution as in the formula above because apparantly VBA does not allow you to concat multiple criteria with &.
My current solution of using tons of CountIfs is not exactly satisfying and completely a lot more resource intensive than necessary (and messy!):
resultCount = Application.WorksheetFunction.SumIfs(dataWorkBook.Range("A" & departmentStartRow & ":BC" & departmentEndRow).Columns(weekNumber + 1), dataWorkBook.Range("B" & departmentStartRow & ":B" & departmentEndRow), StorLoc, dataWorkBook.Range("C" & departmentStartRow & ":C" & departmentEndRow), MatGroup)
Please, try the next code. It uses an array and should be very fast even for big ranges, working in memory:
Sub LookupThreeConditions()
Dim sh As Worksheet, sh1 As Worksheet, lastRow As Long, arr, i As Long
Dim dep As String, stLoc As String, matGr As String, result As Long
dep = "1101": stLoc = "0001": matGr = "1225"
Set sh = ActiveSheet
Set sh1 = sh.Next 'use here the sheet you need to copy in
lastRow = sh.Range("A" & sh.rows.Count).End(xlUp).row
arr = sh.Range("A2:F" & lastRow).value 'put the range in an array
For i = 1 To UBound(arr)
If arr(i, 1) & arr(i, 2) & arr(i, 3) = dep & stLoc & matGr Then
result = arr(i, 5) + arr(i, 6): Exit For
End If
Next i
MsgBox "Result is " & result
sh1.Range("A1").value = result 'use here what range you need
End Sub

Sequencing a part number using User Form

I am completely new in VBA or programming. Right now I am developing a macro for a manufacturing site that inputs process data using Excel's User Forms. One of the things I want this macro to do is to automatically create run numbers for each process. The run number syntax we use is as follows:
V1.yy.mm.dd-1
V1.yy.mm.dd-2
V1.yy.mm.dd-3
Ex V1.20.04.29-1
The way I am trying to set up the run number creation is that when I select an item from a ComboBox the part number gets created into a TextBox to later be submitted into the corresponding database. I am not sure how to create a sequence after the Prefix = V1.yy.mm.dd-, I tried to use a CountIf application that would count the number of Prefixes with the same date in the spreadsheet for sequencing, but it seems the function does not work for partial matches. I tried to use the following but I can't get it to work. I am sure there are simpler ways to do this, can you give me a few suggestions? Thanks
This is the code I wrote so far:
Private Sub ComboBox1_Change()
If Me.ComboBox1.Value <> "" Then
Dim Prefix As String
Dim mm, dd, yy As String
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("2- V1 Loading (2)")
Dim s As Long
s = 1 + sh.Application.Count(Application.Match(Prefix, Range("B:B"), 0))
mm = Format(Date, "mm")
dd = Format(Date, "dd")
yy = Format(Date, "yy")
Prefix = "V1." & yy & "." & mm & "." & dd & "-"
v1 = "V1." & yy & "." & mm & "." & dd & "-" & "" & s
Me.TextBox6.Value = v1
End If
Maybe something like this ?
Private Sub ComboBox1_Change()
If Me.ComboBox1.Value <> "" Then
Set sh = ThisWorkbook.Sheets("2- V1 Loading (2)")
oDate = Format(Date, "yy.mm.dd")
oConst = "V1." & oDate & "-"
Range("B1:B10000").Copy Destination:=Range("zz1") 'copy all the item to helper column
Range("zz:zz").Replace What:=oConst, Replacement:="" 'get only the number from all the items with the current date
nextNum = Application.Max(Range("zz:zz")) + 1 'get the next number
MsgBox oConst & CStr(nextNum) 'this line only for checking
Range("zz:zz").ClearContents 'clear the helper column
Me.TextBox6.Value = oConst & CStr(nextNum)
End If
But this assuming that the item number in columns B is only at the same day.
If for example there is a forgotten data from any day before the current day, and this want to be inputted with that day and the next number, it need an input box or maybe a cell in sheet where the value is that day, then it will give the last number of that day.
Suppose the data in column B is something like below:
If the code is run today, it will show V1.20.04.30-4 as the next number. With the same data like above, if the code is run tomorrow, it will give V1.20.05.01-1.
To get the next number from yesterday (29 Apr 2020), the code need more line - which is to know on what day the code must get the next number.
Or this kind of line maybe is shorter:
oConst = "V1." & Format(Date, "yy.mm.dd") & "-"
nextNum = oConst & Application.WorksheetFunction.CountIf(Range("B:B"), "*" & oConst & "*") + 1
MsgBox nextNum
There are a few ways you could go about this but I'd say the easiest would be to put the incrementing run number in a separate cell somewhere on your worksheet (or another one if you want) to reference each time.
For example:
When the data is entered onto your 'database' sheet, write the run value to ThisWorkbook.Sheets("2- V1 Loading (2)").Range("AZ1").
Then in your code check that value like so:
Private Sub ComboBox1_Change()
If Me.ComboBox1.Value <> "" Then
Dim Prefix As String
Dim mm, dd, yy As String
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("2- V1 Loading (2)")
Dim s As Long
s = 1 + sh.Range("AZ1").Value
mm = Format(Date, "mm")
dd = Format(Date, "dd")
yy = Format(Date, "yy")
Prefix = "V1." & yy & "." & mm & "." & dd & "-"
v1 = "V1." & yy & "." & mm & "." & dd & "-" & s
Me.TextBox6.Value = v1
Presuming that the reference numbers are written to column B of the 2- V1 Loading (2) tab then the next number must always be the one found at the bottom of the column + 1. If there is no number for that date than the new sequential number should be 1. The code below implements that method
Function NextRef() As String
' 016
Dim Fun As String
Dim Counter As Integer
Dim Rng As Range
Dim Fnd As Range
Dim Sp() As String
Fun = Format(Date, """V1.""yy.mm.dd")
With ThisWorkbook.Worksheets("2- V1 Loading (2)")
' start in row 2 (row 1 holding column captions)
Set Rng = .Range(.Cells(2, "B"), .Cells(.Rows.Count, "B").End(xlUp))
End With
If Rng.Row > 1 Then ' skip, if the column is empty
' finds the first occurrence of Ref from the bottom
Set Fnd = Rng.Find(What:=Fun, _
After:=Rng.Cells(1), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchDirection:=xlPrevious)
If Not Fnd Is Nothing Then
Sp = Split(Fnd.Value, "-")
If UBound(Sp) Then Counter = Val(Sp(1))
End If
End If
NextRef = Fun & -(Counter + 1)
End Function
You can use the function simply like ComboBox1.Value = NextRef. However when and how to call that line of code is a bit unclear in your design as published. Especially, it's not clear why you would want it in a ComboBox at all, given that the box might also contain other information. Your idea to use the Change event may not work as intended because that event occurs with every letter the user types. I have tested this:-
Private Sub ComboBox1_GotFocus()
' 016
With ComboBox1
If .Value = "" Then .Value = NextRef
End With
End Sub
The next reference number is inserted as soon as you click on the ComboBox. It works but it doesn't make sense. I think now that you have the function that does the work you will find a way to deploy it. Good luck.

Excel VBA macro reading one column with differing text

I was tasked with creating a code that will check to see if internal hyperlinks in an excel spreadsheet worked. This code first changes the formulas that were on the spreadsheet and makes them actual hyperlinks (they were originally formulas linking the locations together). The problem that I have now is that I want to create hyperlinks ONLY if Column S has text. If it doesn't, I don't want the "E-COPY" text to be displayed. All of the text in Column S varies (not one line has the same characters), which is why I'm drawing a blank is to how I tell the program to only continue if it has any text, not anything specific. I am working with Excel 2016.
Also, I am doing this to 71935 and counting rows; is there a limit to how many it can go through? If so, what can I do about it?
Thank you!
Sub CreateHyperlinks()
Dim FN As Variant
Dim Path As Variant
Dim count As Variant
Sheets(1).Activate
count = WorksheetFunction.CountA(Sheets(1).Range("A:A"))
For i = 2 To count
If Range("AM" & i).Value = "Yes" And Columns("S") = Then
Range("E" & i).Value = ""
Path = Sheets(1).Range("R" & i).Value
FN = Sheets(1).Range("S" & i).Value
Sheets(1).Range("E" & i).Select
Selection.ClearFormats
Selection.Hyperlinks.Add Anchor:=Selection, Address:=Path & FN, TextToDisplay:="E-COPY"
Range("AM" & i).Value = " "
End If
Next i
End Sub
If you just need to check for any content in ColS then:
If Range("AM" & i).Value = "Yes" And Len(Range("S" & i).Value) > 0 Then
Few things:
'make a reference to the sheet you're working with
Dim ws As Worksheet
Dim wb As Workbook
Set wb = Excel.Application.ThisWorkbook
Set ws = wb.Worksheets(1)
'gets the absolute last row with data in it // ignores empty cells
count = ws.UsedRange.Rows.Count
personally, i hate working with named ranges, so i would suggest setting range references like so
what you wrote
Path = Sheets(1).Range("R" & i).Value
what i believe it should look like
Path = ws.Cells(i, 18).Value
if you want to test the type when working with variants, try this:
'tests the type associated with the variant. an 8 = string
If VarType(ws.Cells(i, 19).Value) = 8 Then
'do your thing
'tests if the value is null
ElseIf VarType(ws.Cells(i, 19).Value) = 0 Then
'do your other thing
here's a list of the vartype enumeration to help you out.
hope it helps!

pasting in vba data

image worksheetI am setting up sheet with hotels details and column "D" has hospitals that are close by eg PMH,SCGH,FSH. What i am trying to do is search column "D" based on a cell value on same sheet. I have code below but it will only do what i want if the cells in column"D" are single entry eg pmh. I need to be able to search all the cells in Column "D" for any instance of the text.
Many Thanks for any assistance
`Option Explicit
Sub finddata()
Dim hospitalname As String
Dim finalrow As Integer
Dim i As Integer
Sheets("Results").Range("A4:D100").ClearContents
Sheets("Main").Select
hospitalname = Sheets("Main").Range("g3").Value
finalrow = Sheets("Main").Range("A1000").End(xlUp).Row
For i = 2 To finalrow
If Cells(i, 4) = hospitalname Then
Range(Cells(i, 1), Cells(i, 4)).Copy
Sheets("Results").Range("A4").End(xlUp).Offset(1, 0).PasteSpecial xlPasteFormulasAndNumberFormats
End If
Next i
Sheets("Main").Range("g3").Select
End Sub
`
The two simplest ways to do this would be
Using the Like operator:
If Cells(i, 4).Value Like "*" & hospitalname & "*" Then
This method has the drawback that a hospital name of, for instance, PMH might be matched against another one such as SPMH.
Using the InStr function:
If Instr("," & Cells(i, 4).Value & ",", "," & hospitalname & ",") > 0 Then
In this line, I "wrap" both the cell being looked at, and the value being searched for, within commas so it ends up searching for the string (for instance) ",PMH," within the string ",PMH,SCGH,FSH,". InStr will return the character position at which a match occurs, or zero if no match is found. So testing for > 0 is testing whether a match occurred.

Concatenating SumIfs in VBA

I'm trying to get this segment of code to execute. This is a simplified version of the code. I've included the relevant code. I'm trying to concatenate strings and named ranges into a SumIfs formula, but I get error 1004 "Application-defined or Object-defined error." I have a working line of code above this problem section that is similar with the exception of doing a sum function, instead of sumif. Any idea how to get this code to execute? Thank you.
Dim wb As Workbook
Dim ara As Worksheet
Dim inv As Worksheet
Dim ARBlock As Range
Dim Invoices As Range
Dim AgedDays As Range
Set wb = ThisWorkbook
Set ara = wb.Sheets("AR Aging")
Set inv = wb.Sheets("Invoices")
Set ARBlock = ara.Range("a6")
Set Invoices = inv.Range("a6", inv.Range("a6").End(xlDown))
Set AgedDays = Invoices.Offset(0, 6)
'Populate A/R age buckets
For i = 6 To ARBlock.Rows.Count + 6
With ARBlock(i - 5, 1).Offset(0, 3)
.Value = "=SumIfs(" & Invoices.Offset(0, 4).Address & "," & _
Invoices.Address & "," & ARBlock(i - 5, 1).Value & "," & _
Invoices.Offset(0, 6).Address & ","" <= "" & &O30)"
End With
Next i
End Sub
The line beginning with ".value" is where I'm getting the error message. P.S.: I need the cell to contain the concatenated formula, as opposed to the output value.
UPDATE 1:
As some suggested I updated the .value line to:
.Offset(0, 3).Formula = "=SumIfs(Invoices.Offset(0, 4).Address,Invoices.Address,ARBlock.cells(i - 5, 1).Value)"
I'm still getting the same error. Some auditing I've done:
Removing the "=" before "Sumifs" allows the code to run fine; pasting in the formula into the target cell as text. In this form, my output for i=1 goes to ARBlock.cells(1,1), as it should.
I also used Debug.Print to view all of the components of the formula:
Debug.Print ARBlock.Cells(i - 5, 1).Address
'output $A$6
Debug.Print ARBlock.Cells(i - 5, 1).Value
' output International Business Machines
Debug.Print Invoices.Offset(0, 4).Address
'output $E$6:$E$255
Debug.Print Invoices.Address
'output $A$6:$A$255
I suspected the issue might be that the range dimensions might have been off, but this is not the case. My next suspicion is that the output International Business Machines needs to be in " " for the formula to read it correctly. I hardcoded in
""International Business Machines""
to see if this would fix the formula, but I keep getting the same error once I add the "=" back in. The formula syntax is correct, the dimensions are the same between the sum range and criteria range. Anyone else have any ideas?
.Offset(0, 3).Formula = "=SumIfs(Invoices.Offset(0, 4).Address,Invoices.Address,ARBlock.cells(i - 5, 1).Value)"
Change to:
.Offset(0, 3).Formula = "=SumIfs(" & Invoices.Offset(0, 4).Address & ", " & Invoices.Address & ", " & chr(34) & ARBlock.Cells(i - 5, 1).Value & chr(34) & ")"
EDIT: Added quotes chr(34) around your string!
Your ARBlock(i - 5, 1).Value most likely is an empty cell, which messes the SUMIFS formula as it builds it with to consecutive commas

Resources