Calling VBA custom concatenate function in main Sub - excel

I have a set of data as below, where the first row for each Reference number is a summary of all rows below it:
Reference
Date
Comment
Hidden, Concatenated
1
20/6/22
1
20/6/22
Test
20/6: Test
1
18/6/22
Test2
18/6: Test2
2
20/6/22
3
16/6/22
Where the Hidden, Concatenated column is done using the built-in Excel =CONCAT function, as =CONCAT(TEXT([#[Date]],"dd/mm"),": ",[#Comment]).
Also in this, I have a VBA UserForm in which the Reference is selected from a ComboBox, and Comments added in a TextBox. This inserts a new row directly underneath the summary row, with the date of the note entry and the comment populated into this new row.
I have used the UDF provided in https://www.extendoffice.com/documents/excel/2723-excel-concatenate-based-on-criteria.html, which works well, but takes a long time, as this is a long document.
The end aim is for the cell C2 to equal 20/6: Test, 18/6: Test2
I have been using the UDF:
ConcatenateIf(CriteriaRange As Range, Condition As Variant, _
ConcatenateRange As Range, Optional Separator As String = ", ") As Variant
as =ConcatenateIf(A:A,[#[Reference]],D:D) in C2.
I have done a workaround now of limiting the range to A2:A500 and D2:D500, but is it possible to call this function within the sub that inserts the new line? I.e. Not to insert the function as ws.Cells("C2").Formula = "=ConcatenateIf(A:A,[#[Reference]],D:D, but to have the Value of the function inserted?
Thank you in advance.

Related

VBA - Excel - How to write a formula in a cell instead of values using list objects

I'm struggling with VBA and couldn't find a solution on internet.
This is what I'm trying to do.
I have an excel file with two sheets
First sheet is an extract from a Database (with many columns, an particulary one called "country"). It is formated as a ListObject
Second sheet is a Calculator that is counting the number of occurence of every country in the other sheet. It is also formated as a list object
For Example at the end I'll get:
France: 10
USA: 25
Italia: 30
I found how to calculate this using the Application.WorksheetFunction.CountIf function.
Now my question is: Is it possible to write the formula in the cell, instead of writing the result ?
The objectif is to generate a sheet filled with formulas, so that when I change things in the Database sheet, then it will automatically recalculate my results.
This is my current code: It works, but I'd like to write a formula in the cell, instead of the result.
It there an easyway to do this ?
Thanks a lof for your help !
Regards,
J.
Dim i As Long
Dim CountryName As Variant
Dim KPI_LO As ListObject
Dim REVIEWEDDATA_LO As ListObject
'This is the list object with the database information
Set REVIEWEDDATA_LO = Worksheets(REVIEWEDDATA_SHEET_NAME).ListObjects(REVIEWEDDATA_LISTOBJECT_NAME)
'This is the list object where I'll store my results
Set KPI_LO = Worksheets(WASPKPI_SHEET_NAME).ListObjects(WASPKPI_LISTOBJECT_NAME)
'loop through each country column in the result sheet
For i = LBound(GetCountriesList) To UBound(GetCountriesList)
'Get the name of the first country to find
CountryName = GetCountriesList(i)
'Count the number of occurence of this country in the column called COL_COUNTRY in the revieweddata List Object (in my database sheet).
'This is what I'm trying to replace. I want to write a formula that do this calculation in the cell, instead of the result.
KPI_LO.ListColumns(CountryName).DataBodyRange.Rows(1) = Application.WorksheetFunction.CountIf(REVIEWEDDATA_LO.ListColumns(COL_COUNTRY).DataBodyRange, CountryName)
Next i
if you want to see the formula in a cell, then you can create a function and then just call that and pass through your values.
For example, create a module:
Function fn_Multiply(x As Double, y As Double) As Double
Dim z As Double
z = x * y
fn_Multiply = z
End Function
And use it in excel as a formula like this:
=fn_Multiply(A2,B2)
enter image description here

Excel: define a VBA function to shorten PERCENTRANK

In Excel formula become often very long and, consequently, unreadable. For example my formula looks like this:
=PERCENTRANK([vol];[#vol])-PERCENTRANK([pos];[#pos])
I would like to write just
=SCORE("vol", "pos")
How can I create a user defined function from the original worksheet formula?
You can do this relatively easily using VBA's WorksheetFunction. The most difficult part is actually getting the column of the [#Range] so you don't have to enter it - but that's not too complicated either.
In the VBE, create a new standard module and paste the following UDF:
Public Function SCORE(vol As Range, pos As Range) As Double
' We need to get the entire column of the table for each argument
Dim tbl As ListObject, volRng As Range, posRng As Range
Set tbl = vol.ListObject
Rem -> Table columns are relative to the table - not the worksheet
Set volRng = tbl.Range.Columns(vol.Column - tbl.Range.Cells(1).Column + 1)
Set posRng = tbl.Range.Columns(pos.Column - tbl.Range.Cells(1).Column + 1)
With Application.WorksheetFunction
SCORE = .PercentRank(volRng, vol) - .PercentRank(posRng, pos)
End With
End Function
You have to keep in mind that table column numbers are not always the same as the worksheet's (if your table doesn't start in column A). So you essentially have to offset by the number of worksheet columns by subtracting the column from your arguments vol and pos from the column number of the first cell of the entire table tbl.range.cells(1). Then you add one because columns numbers in VBA is "base 1".
You will still have to send the range of the individual cell - so using string arguments are not going to do well here. With all that being said, your new worksheet formula would look like:
=SCORE([#vol],[#pos])
or
=SCORE([#vol];[#pos])
depending on if you separate arguments using , or ;.

Sum numbers in one cell that contains line break - Excel

I have been searching for few hours and I couldnt find any solution for this problem.
As you can see on the image I have 3 columns and 2 rows. My goal is to sum the cost row (it has line breaks) or split equipment and cost columns into 3 smaller rows. Is this possible?
Something like this in VBA will work:
Function SumLines(ByVal str As String) As Long
Dim arr() As String
arr = Split(str, Chr(10))
For a = 0 To UBound(arr)
SumLines = SumLines + CLng(arr(a))
Next
End Function
However, this will only work if you don't have any characters other than digits and Chr(10)s (new lines).
You then use this in your worksheet, e.g.:
=SumLines(A1)
Unfortunately there's not a particularly clean way to achieve this (to my knowledge). Here are a couple of things you could try though:
Method 1 - worksheet solution
If your first cost cell is in A1 then place the cursor in cell B1 and create a new named range using the following formula:
=EVALUATE(SUBSTITUTE(A1,CHAR(10),"+"))
You can then type the name of the named range into cell B1 and you'll get the sum as expected. Unfortunately you have to create a named range for this because EVALUATE isn't available as a worksheet function - only a named range function and also available in VBA.
I called my named range "eval". You can drag this formula down and it will fill down, always evaluating the cell to it's left.
Method 2 - VBA solution
You can use some simple VBA. Paste this into a new module and then use this formula on the worksheet like this:
=SumAlt(A1)
will return 600 in your example, if A1 contained your 100 200 300
Function SumAlt(s As String) As Long
SumAlt = Evaluate(Replace(s, Chr(10), "+"))
End Function

SumIf with Strings?

This may be a stupid question, and if it is, I apologise. I have a table in excel:
Column a...........Column b
1 property1.......problem x
2 property2.......problemy
3 property3.......problemz
4 property1......problem a
I was wondering if I could use sumif (or any similar formula) to add the problems, referring to a certain property, in one cell. for ex:
I would have
Column a...........Column b
1 property1.......problem x problem a
The problem is I can't figure out where to start. I tried using sumif but I get an error. Probably because I'm trying to add strings. I tried to mix a vlookup with sumif but that didn't produce anything too. Im stuck here. Thanks for any help!
I am not 100 % sure, but I think you might need to use VBA for this. You could try to create the following custom function:
Create named ranges properties and problems in your sheet.
Click ALT+F11 to open VBA editor.
Press Insert --> Module
Write code
'
Function ConcatIF(Property As String, PropertyRange As Range, ProblemRange As Range) As String
Dim counter As Integer
Dim result As String
result = ""
Dim row As Range
counter = 1
For Each row In PropertyRange.Rows
If (Property = row.Cells(1,1).Value) Then
result = result + ProblemRange.Cells(counter,1).Value + " "
End If
counter = counter +1
Next row
ConcatIF = result
End Function
As I do not have excel on the machine I am writing this on, I could only test it on another machine, and therefore there could be spelling mistakes in this code.
Ensure that you write the code in the module you created, it cannot be written in a Sheet's code, must be module.
This function can then be called as a regular function, like sum, average and if. Create a unique list of all your properties on another sheet. Properties in column A, and then in column B you can call the cuntion. Assume row 1 is used for headings, write the following and copy down.
=ConcatIF(A2,properties,problems)
NOTE!!!! This code gets out of hand very quickly. It needs to do (number of properties) x (number of property/problem pairs) comparisons, so if this number is huge, it could slow down your sheet.
There could be faster methods, but this was from the top of my head.

Excel "UnCONCATENATE"/explode in function / Convert cell into array

I am trying to "Unconcatenate" a string in Excel 2010. Yes, I know that is not a real word. So pretty much; I have a cell that can not be split into multiple columns the cell looks like this:
Item 1, Item 2, Item 3
Now this cell may have 0-? items. I am wanting to compare that one cell against a column in another sheet. I believe I would need to use the match function to do this, but I need that first cell to be converted into an array in a function with the delimiter as the comma.
So far I have =MATCH(Each item in cell, SHEET2!A:A, 0)
Any help would be nice. I am aware of =Left and =Right, but I do not think these will work because the number of items in each cell may not be the same. Thanks
Edit:
Detailed discription:
In my first sheet I have a dropdown box. When you choose items it does a vlookup on sheet 2 on this item. When this happens I want it to also check if cell E in that row (item 1, item 2, item 3) match any of the individual cells in a column in sheet 3
The following code exposes VBA's Split function for worksheet use--it returns a row array of items that have been split using a specified delimiter. For example, if cell A1 contained the text "Item 1,Item 2"), EXPLODE(A1,",") would return an array with elements "Item 1" and "Item 2".
Function EXPLODE(str As String, Optional delimiter As Variant) As Variant
If IsMissing(delimiter) Then
delimiter = " "
End If
EXPLODE = Split(str, delimiter)
End Function
It is an array function. To use the returned elements in the spreadsheet:
Select the cells in which you want the "exploded" items show
Enter the function specifying the cell with the source string (or reference to the cell which contains the source) and the delimiter on which the split will be done
Complete the entry using the Control-Shift-Enter key combination.
Alternatively, individual elements can be chosen using the INDEX function--=INDEX(EXPLODE(A1,1,2) would return "Item 2" using the previous example. (Given a range or array, the INDEX function returns the value in the ith row and jth column.) This usage does not require the formula to be entered as an array formula.
For your use case, a combination with other functions would be in order. You have a string with multiple items of the form "aa, bb, cc" (the result of a VLOOKUP) and want to determine whether any of the elements of this string can be found as individual items in any of the cells in column A. You want a function that will return True if all of the elements are found, and False otherwise. The following formula achieves that result:
=SUM(SIGN(IFERROR(MATCH(TRIM(EXPLODE(D1,",")),$A:$A,0),0)))=COUNTA(EXPLODE(D1,","))
It is an array formula and needs to be entered with Control-Shift-Enter. Note that I used the contents of cell D1 in lieu of your lookup value. The TRIM function strips out any extraneous spaces between the elements of the string with multiple items.
(not really an answer, just trying to figure out what the question is)
Sheet 1 has a dropdown box with a number of items in, the selected item is used in a vlookup() looking at a table in sheet 2.
Sheet 2 has 2(+) columns, one which is an index used for a vlookup and the other which contains delimited lists.
Sheet 3 has 1(+) columns, each row has a value that may correspond to an item in one of the delimited lists in sheet 2.
When an item is selected in the dropdown box on sheet 1 I want to lookup the corresponding list in sheet 2 (using the vlookup) and then see if any of the items in that list exist in sheet 3.
Is this what your trying to do? If yes, what is the result of this search?
Boolean: True-Some matches found!, False-No Matches
Number: I found this many results
No? :(
Update
Doing that with just worksheet functions would be fairly tricky!
VBA is a much better choice. (the final step atleast)
Add the following code to a new module (not a worksheet or workbook module) and it will be available as a UDF on your worksheets.
This function takes a string (which is your delimited list) it is exploded inside the function so you dont have to worry about doing it.
I've not tested it, but in theory it should allow you to pass it a list. The function then should check sheet 3 for you and return true/false depending on weather the items are found or not.
I know that you've found an answer, but here is a working and slightly faster version of my function.
Public Function ValidateList(ByVal Target As Range) As Boolean
Dim Sheet As Worksheet: Set Sheet = ThisWorkbook.Worksheets("Sheet3") ' Check Sheet3 is the correct sheet
Dim List As Variant: List = Sheet.UsedRange.Columns("A").Value2 ' Set Column(A) to correct column
Dim Items() As String: Items = Split(Target.Value2, ",")
Dim Item As Long
Dim Key As String
Dim Result As Boolean: Result = False
Dim Search As Object: Set Search = CreateObject("Scripting.Dictionary")
For Item = LBound(Items) To UBound(Items)
Search.Add Trim(Items(Item)), False
Next Item
If Search.Count > 0 Then
' Target List has 1+ Items
For Item = LBound(List, 1) To UBound(List, 1)
Key = Trim(List(Item, 1))
If Search.Exists(Key) = True Then
Search.Remove Key
End If
If Search.Count = 0 Then
Result = True
Exit For
End If
Next Item
Else
' Target List is Empty
' Optionally set result to True here if empty list should return True
' Defaults to False
End If
ValidateList = Result
End Function

Resources