Finding cell reference in For loop - regex

I am running a regex text to find invalid data in a range. I want an error message box to display the location of the error when it is found as the data sets are large.
So far my for loop is:
For Each cell In rng1
Dim vntData As Variant
vntData = cell
If Not (regtext.test(vntData) Or cell.value = "") Then
MsgBox "Failed to validate data [" & vntData & "] in cell E" & n & ".", vbExclamation, "Regular Expressions"
cell.Select
Exit For
End If
If n < 100000000 Then
n = n + 1
End If
Next cell
This does the required job. However I would prefer to have a general cell reference (rather than specifying "E") since I need to use it in several different cases.
Is there a way to get the cell reference from the variable "cell" in this case?

Use cell.address. MSDN docs on this method are here, or see the VBA help file within Excel.

Related

Google Sheets: How can I extract partial text from a string based on a column of different options?

Goal: I have a bunch of keywords I'd like to categorise automatically based on topic parameters I set. Categories that match must be in the same column so the keyword data can be filtered.
e.g. If I have "Puppies" as a first topic, it shouldn't appear as a secondary or third topic otherwise the data cannot be filtered as needed.
Example Data: https://docs.google.com/spreadsheets/d/1TWYepApOtWDlwoTP8zkaflD7AoxD_LZ4PxssSpFlrWQ/edit?usp=sharing
Video: https://drive.google.com/file/d/11T5hhyestKRY4GpuwC7RF6tx-xQudNok/view?usp=sharing
Parameters Tab: I will add words in columns D-F that change based on the keyword data set and there will often be hundreds, if not thousands, of options for larger data sets.
Categories Tab: I'd like to have a formula or script that goes down the columns D-F in Parameters and fills in a corresponding value (in Categories! columns D-F respectively) based on partial match with column B or C (makes no difference to me if there's a delimiter like a space or not. Final data sheet should only have one of these columns though).
Things I've Tried:
I've tried a bunch of things. Nested IF formula with regexmatch works but seems clunky.
e.g. this formula in Categories! column D
=IF(REGEXMATCH($B2,LOWER(Parameters!$D$3)),Parameters!$D$3,IF(REGEXMATCH($B2,LOWER(Parameters!$D$4)),Parameters!$D$4,""))
I nested more statements changing out to the next cell in Parameters!D column (as in , manually adding $D$5, $D$6 etc) but this seems inefficient for a list thousands of words long. e.g. third topic will get very long once all dog breed types are added.
Any tips?
Functionality I haven't worked out:
if a string in Categories B or C contains more than one topic in the parameters I set out, is there a way I can have the first 2 to show instead of just the first one?
e.g. Cell A14 in Categories, how can I get a formula/automation to add both "Akita" & "German Shepherd" into the third topic? Concatenation with a CHAR(10) to add to new line is ideal format here. There will be other keywords that won't have both in there in which case these values will just show up individually.
Since this data set has a bunch of mixed breeds and all breeds are added as a third topic, it would be great to differentiate interest in mixes vs pure breeds without confusion.
Any ideas will be greatly appreciated! Also, I'm open to variations in layout and functionality of the spreadsheet in case you have a more creative solution. I just care about efficiently automating a tedious task!!
Try using custom function:
To create custom function:
1.Create or open a spreadsheet in Google Sheets.
2.Select the menu item Tools > Script editor.
3.Delete any code in the script editor and copy and paste the code below into the script editor.
4.At the top, click Save save.
To use custom function:
1.Click the cell where you want to use the function.
2.Type an equals sign (=) followed by the function name and any input value — for example, =DOUBLE(A1) — and press Enter.
3.The cell will momentarily display Loading..., then return the result.
Code:
function matchTopic(p, str) {
var params = p.flat(); //Convert 2d array into 1d
var buildRegex = params.map(i => '(' + i + ')').join('|'); //convert array into series of capturing groups. Example (Dog)|(Puppies)
var regex = new RegExp(buildRegex,"gi");
var results = str.match(regex);
if(results){
// The for loops below will convert the first character of each word to Uppercase
for(var i = 0 ; i < results.length ; i++){
var words = results[i].split(" ");
for (let j = 0; j < words.length; j++) {
words[j] = words[j][0].toUpperCase() + words[j].substr(1);
}
results[i] = words.join(" ");
}
return results.join(","); //return with comma separator
}else{
return ""; //return blank if result is null
}
}
Example Usage:
Parameters:
First Topic:
Second Topic:
Third Topic:
Reference:
Custom Functions
I've added a new sheet ("Erik Help") with separate formulas (highlighted in green currently) for each of your keyword columns. They are each essentially the same except for specific column references, so I'll include only the "First Topic" formula here:
=ArrayFormula({"First Topic";IF(A2:A="",,IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))) & IFERROR(CHAR(10)&REGEXEXTRACT(REGEXREPLACE(LOWER(B2:B&C2:C),IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))),""),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))))})
This formula first creates the header (which can be changed within the formula itself as you like).
The opening IF condition leaves any row in the results column blank if the corresponding cell in Column A of that row is also blank.
JOIN is used to form a concatenated string of all keywords separated by the pipe symbol, which REGEXEXTRACT interprets as OR.
IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))) will attempt to extract any of the keywords from each concatenated string in Columns B and C. If none is found, IFERROR will return null.
Then a second-round attempt is made:
& IFERROR(CHAR(10)&REGEXEXTRACT(REGEXREPLACE(LOWER(B2:B&C2:C),IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))),""),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>"")))))
Only this time, REGEXREPLACE is used to replace the results of the first round with null, thus eliminating them from being found in round two. This will cause any second listing from the JOIN clause to be found, if one exists. Otherwise, IFERROR again returns null for round two.
CHAR(10) is the new-line character.
I've written each of the three formulas to return up to two results for each keyword column. If that is not your intention for "First Topic" and "Second Topic" (i.e., if you only wanted a maximum of one result for each of those columns), just select and delete the entire round-two portion of the formula shown above from the formula in each of those columns.

Excel, duplicates in string, single cell iteration

I'm trying to extract certain pieces of data from a very long string within a single cell. For the sake of this exercise, this is the data I have in cell A1.
a:2:{s:15:"info_buyRequest";a:5:{s:4:"uenc";s:252:"WN0aW9uYWwuaHRlqdyZ2dC1hdD0lN0JhZHR5cGUlN0QmdnQtcHRpPSU3QmFkd29yZHNfcHJvZHVjdHRhcmdldGlkJTdEJiU3Qmlnbm9y,";s:7:"product";s:4:"1253";s:8:"form_key";s:16:"wyfg89N";s:7:"options";a:6:{i:10144;s:5:"73068";i:10145;s:5:"63085";i:10141;s:5:"73059";i:10143;s:5:"73064";i:13340;s:5:"99988";i:10142;s:5:"73063";}s:3:"qty";s:1:"1";}s:7:"options";a:6:{i:0;a:7:{s:5:"label";s:5:"Color";s:5:"value";s:11:"White";s:11:"print_value";s:11:"White";s:9:"option_id";s:5:"10144";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73068";s:11:"custom_view";b:0;}i:1;a:7:{s:5:"label";s:4:"Trim";s:5:"value";s:11:"Black";s:11:"print_value";s:11:"Black";s:9:"option_id";s:5:"10145";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"63085";s:11:"custom_view";b:0;}i:2;a:7:{s:5:"label";s:7:"Material";s:5:"value";s:15:"Vinyl";s:11:"print_value";s:15:"Vinyl";s:9:"option_id";s:5:"10141";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73059";s:11:"custom_view";b:0;}i:3;a:7:{s:5:"label";s:6:"Orientation";s:5:"value";s:17:"Left Side";s:11:"print_value";s:17:"Left Side";s:9:"option_id";s:5:"10143";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73064";s:11:"custom_view";b:0;}i:4;a:7:{s:5:"label";s:12:"Table";s:5:"value";s:16:"YES! Add Table";s:11:"print_value";s:16:"YES! Add Table";s:9:"option_id";s:5:"13340";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"99988";s:11:"custom_view";b:0;}i:5;a:7:{s:5:"label";s:8:"Shipping";s:5:"value";s:20:"Front Door Delivery";s:11:"print_value";s:20:"Front Door Delivery";s:9:"option_id";s:5:"10142";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73063";s:11:"custom_view";b:0;}}}
The end result, would be to separate the values for Color, Trim, Material Orientation, etc.
The formula I was using is this:
=MID(LEFT(A4,FIND("print_value",A4)-9),FIND("Color",A4)+25,LEN(A4))
This basically looks in between two points and trims out the fat. It works, but only for the first iteration of "print_value". If I were to use this searching for "Trim"...
=MID(LEFT(A4,FIND("print_value",A4)-9),FIND("Trim",A4)+25,LEN(A4))
...I get an empty result. This happens because print_value is duplicate and not unique to the string. Excel doesn't understand what point to apply its function to and poops itself.
Even though there are unique factors within this string that I could essentially attach myself to (and arrive at the desired result), I CAN NOT use them as they will not be consistent and will render the formula useless when applied to other cells.
That said, here is what I need. Within this formula, I need a way to either A) tell the formula which iteration of print_value to find or B) change print_value to print_value(1,2,3,4, etc) and then run my trimming formula.
Few options based on this link:
1) VBA - Using a User Defined Function
If you're new to these then follow this tutorial.
Function FindN(sFindWhat As String, _
sInputString As String, N As Integer) As Integer
Dim J As Integer
Application.Volatile
FindN = 0
For J = 1 To N
FindN = InStr(FindN + 1, sInputString, sFindWhat)
If FindN = 0 Then Exit For
Next
End Function
2) Using a Formula
=FIND(CHAR(1),SUBSTITUTE(A1,"c",CHAR(1),3))
c is the character you want to find
A1 is the text you want to look in
3 is the nth instance

Update Data Validation List Range

I'm trying to create a vba code that will update my data validation list range to last row on another sheet.
My dropdown list is on my "Home" tab which takes its list range from column P on "Mapping tab". The list will change daily so wanted to add in the formaula to look for finalrow rather than entering a cell row value. Here is the code I have written but a error occurs with the formula.
Sub getDropdownList()
Dim finalrow1 As Integer
'finds last row in Column P on Mapping tab
Sheets("Mapping").Select
finalrow1 = ActiveSheet.Cells(Rows.Count, "P").End(xlUp).Row
Sheets("Home").Select
Range("E7").Select
With Selection.Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= _
xlBetween, Formula1:="=Mapping!$P$1:$P &finalrow1"
End With
End Sub
No need to use VBA to update the formula - use a dynamic formula (as a named range) instead.
As a named range this will reference from $P$1 to the last cell containing data - providing there are no blank cells in the column P list.
=Mapping!$P$1:INDEX(Mapping!$P:$P,COUNTA(Mapping!$P:$P))
This needs to be entered as a named range as data validation won't accept it.
Did I mention it has to be a named range and not entered directly to the Source box? :)
I have no idea if this will work but try changing the formula to
Formula1:="=Mapping!$P$1:$P" & finalrow1
excel is probably treating your integer as a string and getting confused.
On the VBA you did this line wrong:
xlBetween, Formula1:="=Mapping!$P$1:$P &finalrow1"
Please see correction below for that line in your code:
xlBetween, Formula1:="=Mapping!$P$1:$P" & finalrow1

calling a string as a function in openoffice-calc

I don't normally program in openoffice, but I thought I'd give it a shot since it's convenient for the end user. My problem is the following: I have copied the txt of a command into a cell and modified the command string so that it updates with corrected information. The updated cell output is ex:
INDEX(B4:C101,MATCH(MIN(C4:C101),C4:C101,0),1)
-
This, however, needs to be run as an index function. I tried removing the index and referencing the cell with R2 = B4:C101,MATCH(MIN(C4:C101),C4:C101,0),1, so that would be a cell with =INDEX(R2), but it didn't work. I think it's because each argument needs to be input separately when linking to cells.
Short of rewritng the whole thing in three separate linked cells to update with individual arguments and link the index function column as =INDEX(R1,R2,R3,0), where R1 = B4:C101, R2 = MATCH(MIN(C4:C101),and R3= C4:C101,0),1 is there any way to input a string and run it as if it were all 4 arguments of the index function?
OpenOffice Calc usually uses a semi-colon rather than a comma to separate arguements in a function. You could put both values into R1 (separated by a space) and parse the text out to be used by INDIRECT to generate4 cell/range addresses.
With B4:B101 C4:C101 in R1 this should do.
=INDEX(INDIRECT(LEFT(R1; FIND(" "; R1)-1)); MATCH(MIN(INDIRECT(MID(R1; FIND(" "; R1)+1; 9))); INDIRECT(MID(R1; FIND(" "; R1)+1; 9)); 0))

Dynamic Depending Lists in Separated WorkSheets in VBA (2)

I'm working with 7 dynamic dependent lists, and I thought the best way to automate the process and avoid to arrange anything in a future if I modify the lists was a VBA code.
The VBA code that I started to work on it is posted on: Dynamic Depending Lists in Separated WorkSheets in VBA
That code is just for the 2 first lists.
That's the main table that I have. I want pick lists for the first row only for the yellow columns:
That's the table that I have the lists (they must be dynamic):
The relations between my lists are:
Responsible list and Site list are related with Project list.
The other lists are related with the site list.
Okay. I've got what you are looking for. I solved this issue a few months back in another project. Basically, indirect is no good here because it doesn't work on dynamic named ranges, because they don't produce an actual result, just a formula reference.
First, set up your named ranges on a sheet like so. It's very important that the named ranges be named in the manner I described, as this will feed the code into making your dynamic lists. Also, note, I only wrote out SamplePoints for X1 and T2. If you select other options, the code won't work until you add those named ranges in.
Then assuming input sheet is set up like below:
Place this code in the worksheet change event of your input sheet. What it does is take the value selected in one cell and then appends the appropriate column name to feed that list. So, if Project A is selected and you want to pick a responsible party for project A, it sets the validation in Range("B(whatever row you are on)" to be A_Responsible, thus giving you that list.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim wks As Worksheet
Dim strName As String, strFormula
Dim rng As Range
Set wks = ActiveSheet
With wks
If Target.Row = 1 Then Exit Sub
Select Case Target.Column
Case Is = .Rows(1).Find("Project", lookat:=xlWhole).Column
Set rng = Target.Offset(, 1)
strName = Target.Value
strFormula = "=" & Replace(strName, " ", "_") & "_Responsible"
AddValidation rng, 1, strFormula
'add any more cells that would need validation based on project selection here.
Case Is = .Rows(1).Find("Responsible", lookat:=xlWhole).Column
Set rng = Target.Offset(, 1)
strName = Target.Value
strFormula = "=" & Replace(strName, " ", "_") & "_SamplePoint"
AddValidation rng, 1, strFormula
'add any more cells that would need validation based on responsible selection here.
'Case Is = add any more dependenices here ... and continue with cases for each one
End Select
End With
You will also need this function in a standard module somewhere in your workbook.
Function AddValidation(ByVal rng As Range, ByVal iOperator As Integer, _
ByVal sFormula1 As String, Optional iXlDVType As Integer = 3, _
Optional iAlertStyle As Integer = 1, Optional sFormula2 As String, _
Optional bIgnoreBlank As Boolean = True, Optional bInCellDropDown As Boolean = True, _
Optional sInputTitle As String, Optional sErrorTitle As String, _
Optional sInputMessage As String, Optional sErrorMessage As String, _
Optional bShowInput As Boolean = True, Optional bShowError As Boolean = True)
'==============================================
'Enumaration for ease of use
'XlDVType
'Name Value Description
'xlValidateCustom 7 Data is validated using an arbitrary formula.
'xlValidateDate 4 Date values.
'xlValidateDecimal 2 Numeric values.
'xlValidateInputOnly 0 Validate only when user changes the value.
'xlValidateList 3 Value must be present in a specified list.
'xlValidateTextLength 6 Length of text.
'xlValidateTime 5 Time values.
'xlValidateWholeNumber 1 Whole numeric values.
'AlertStyle
'xlValidAlertInformation 3 Information icon.
'xlValidAlertStop 1 Stop icon.
'xlValidAlertWarning 2 Warning icon.
'Operator
'xlBetween 1 Between. Can be used only if two formulas are provided.
'xlEqual 3 Equal.
'xlGreater 5 Greater than.
'xlGreaterEqual 7 Greater than or equal to.
'xlLess 6 Less than.
'xlLessEqual 8 Less than or equal to.
'xlNotBetween 2 Not between. Can be used only if two formulas are provided.
'xlNotEqual 4 Not equal.
'==============================================
With rng.Validation
.Delete ' delete any existing validation before adding new one
.Add Type:=iXlDVType, AlertStyle:=iAlertStyle, Operator:=iOperator, Formula1:=sFormula1, Formula2:=sFormula2
.IgnoreBlank = bIgnoreBlank
.InCellDropdown = bInCellDropDown
.InputTitle = sInputTitle
.ErrorTitle = sErrorTitle
.InputMessage = sInputMessage
.ErrorMessage = sErrorMessage
.ShowInput = bShowInput
.ShowError = bShowError
End With
End Function