Excel 2013 - Comparing Two Cells - excel-formula

I am comparing two corresponding columns of data. In the first row, it passes because variable A and variable B are in column 2. It does not have to contain the variable starting with C, but it can only be any of the letters contained in cell 1:1. For row three, it does not pass because variable starting with A is not an option within cell 3:1. What conditional formatting would I do for a large data set in Excel 2013. That version of excel is all I have available.

If you had excel 365, it would be much easier. Using 2013, you either have to use FILTERXML to isolate the variables, or hard-code for a certain number of variables. Hard-coding gets long very fast.
I can't check in excel 2013, but it would probably look something like this:
=SUM(--ISERROR(
VLOOKUP(
FILTERXML("<x><y>"&SUBSTITUTE(B1,CHAR(10),"</y><y>")&"</y></x>","//y"),
FILTERXML("<x><y>"&SUBSTITUTE(A1,CHAR(10),"</y><y>")&"</y></x>","//y"),
1,FALSE)
))>0
Where the first FILTERXML creates an array of variables from cell B1, the second from cell A1. VLOOKUP finds matches for items in column B to items in column A, and SUM(--ISERROR(... counts how many non-matches are found.
For excel 365 it could look like this:
=LET(
Txt_1, $A1,
Txt_2, $B1,
Delim, CHAR(10),
SeqChar1, SEQUENCE(LEN(Txt_1)),
CharArr1, MID(Txt_1, SeqChar1, 1),
CharVarStartArr1, FILTER(SeqChar1, IF((CharArr1 = Delim) + (SeqChar1 = 1), TRUE, FALSE)),
CharVarLenArr1, FILTER(SeqChar1, IF((CharArr1 = Delim) + (SeqChar1 = LEN(Txt_1)), TRUE, FALSE)) - CharVarStartArr1 + 1,
VarArr1, SUBSTITUTE(MID(Txt_1, CharVarStartArr1, CharVarLenArr1), Delim, ""),
SeqChar2, SEQUENCE(LEN(Txt_2)),
CharArr2, MID(Txt_2, SeqChar2, 1),
CharVarStartArr2, FILTER(SeqChar2, IF((CharArr2 = Delim) + (SeqChar2 = 1), TRUE, FALSE)),
CharVarLenArr2, FILTER(SeqChar2, IF((CharArr2 = Delim) + (SeqChar2 = LEN(Txt_2)), TRUE, FALSE)) - CharVarStartArr2 + 1,
VarArr2, SUBSTITUTE(MID(Txt_2, CharVarStartArr2, CharVarLenArr2), Delim, ""),
OR(IF(ISERROR(XLOOKUP(VarArr2, VarArr1, VarArr1, NA())), TRUE, FALSE))
)
SeqChar1 creates an array of index locations for each string character using SEQUENCE.
CharArr1 creates an array of (single) characters. I'm using MID with array SeqChar1 to output the results as an array.
CharVarStartArr1 is an array with the starting point of each variable. This is done by getting rid of every position that doesn't correspond to a delim character. I also included the first character or we would be missing the first variable.
CharVarLenArr2 finds the approximate string length by subtracting the starting index from the index of the next variable. I included the index of the last string character to get the length for the last variable.
VarArr1 is an array containing each variable. Every variable but the first one also contains the delim character, so I remove them with SUBSTITUTE.

Related

How can I replace multiple string at once in Excel?

The function I expected
some_function(original_text, "search_text", "replacement_text")
The value of the second & third parameters will be multiple characters. For example. The result will replace the character based on the location of the character at the second & third parameters
some_function("9528", "1234567890", "abcdefghij")
1 -> a
2 -> b
3 -> c
...
8 -> h
9 -> i
0 -> j
The result of some_function will be iebh. The nested SUBSTITUTE function can archive the goal but I hope to compact the complexity.
The way you described your requirement is best written out via REDUCE(), a lambda-related helper function and recently announced to be in production:
=REDUCE("9528",SEQUENCE(10),LAMBDA(x,y,SUBSTITUTE(x,MID("1234567890",y,1),MID("abcdefghij",y,1))))
Needless to say, this would become more vivid when used with cell-references:
Formula in A3:
=REDUCE(A1,SEQUENCE(LEN(B1)),LAMBDA(x,y,SUBSTITUTE(x,MID(B1,y,1),MID(C1,y,1))))
Another, more convoluted way, could be:
=LET(A,9528,B,1234567890,C,"abcdefghij",D,MID(A,SEQUENCE(LEN(A)),1),CONCAT(IFERROR(MID(C,FIND(D,B),1),D)))
Or, as per the sceenshot above:
=LET(A,A1,B,B1,C,C1,D,MID(A,SEQUENCE(LEN(A)),1),CONCAT(IFERROR(MID(C,FIND(D,B),1),D)))
Function Multi_Replace(Original As String, Search_Text As String, Replace_With As String) As String
'intEnd represents the last character being replaced
Dim intEnd As Long: intEnd = WorksheetFunction.Min(Len(Search_Text), Len(Replace_With))
'necessary if Search text and replace text are different lengths;
Dim intChar As Long 'to track which character we're replacing
'Replace each character individually
For intChar = 1 To intEnd
Original = Replace(Original, Mid(Search_Text, intChar, 1), Mid(Replace_With, intChar, 1))
Next
Multi_Replace = Original
End Function
Maybe simpler if you do not have lambda yet: =TEXTJOIN(,,CHAR(96+MID(A1,SEQUENCE(LEN(A1)),1)))
*Note that this will not return 0 as the expected result.
Let's say you have a list of countries in column A and aim to replace all the abbreviations with the corresponding full names. you start with inputting the "Find" and "Replace" items in separate columns (D and E respectively), and then enter this formula in B2:
=XLOOKUP(A2, $D$2:$D$4, $E$2:$E$4, A2)
Translated from the Excel language into the human language, here's what the formula does:
Search for the A2 value (lookup_value) in D2:D4 (lookup_array) and return a match from E2:E4 (return_array). If not found, pull the original value from A2.
Double-click the fill handle to get the formula copied to the below cells, and the result won't keep you waiting:
Since the XLOOKUP function is only available in Excel 365, the above formula won't work in earlier versions. However, you can easily mimic this behavior with a combination of IFERROR or IFNA and VLOOKUP:
=IFNA(VLOOKUP(A2, $D$2:$E$4, 2, FALSE), A2)

excel formula to find maximum within same cell have multiple values of right most numbers

I want to find maximum and minimum for right most number from same cell having multiple values, below is the how data looks.
This all values are in same cell, ex: in cell "A1"
I want to extract rightmost number, like 1.00,1.5,1.5,1.0,2.00,1.50,1.0,1.00 and find max and min from it.
I am currently using Excel 2016 version.
BAEK1928_TXL_1.00
44444922_FLR2X-A_1.5
44447922_FL2X-B_1.5
44444922_FL2X-B_1.0
BAEK1928_TXL_2.00
44444922_FLR2X-B_1.50
44444922_FLR2X-A_1.0
44444922_FLRY-B_1.00
Is there any excel formula or vba code for it?
I have tried some excel formula as below but it doesn't work as required.
=MAX(RIGHT(A1,LEN(A1)-FIND("_",A1,SEARCH("_",A1)+1))
Data samples below where the code not work:
KMO6722-1_THICK_0.50
MAEJ9120_GXL_0.50
BIN76822_FLRY-B_0.50
KMO6722-1_THIN_0.50
MAEJ9120_TXL_0.50
KL STLE 2987_0.5
MAEJ9120_SXL_0.50
DIN76722_FLRY-A_0.50
MAEJ9120_TXL_1.00
KMO6722-1_FLR2X-B_1.00
MAEJ9120_GXL_1.00
BIN76822_FL2X-B_1.0
MAEJ9120_GXL_0.50
MAEJ9120_GXL_0.80
BIN76822_FLR2X-A_1.0
BIN76822_FLRY-B_1.00
MAEJ9120_TXL_0.80
KMO6722-1_FLR2X-B
MIL-STD-104_PTFE_2.0
BIN76822 _THIN_1.00
BIN76822 _THIN_2.00
MIL-STD-104_PTFE_1.0
MAEJ9120_TXL_0.80
BIN76822_FLRY-B_1.50
BIN76822-1_FLR2X-B
MAEJ9120_TXL_1.00_Sn
PLAIN_EXRD_1.00
MAEJ9120_TXL_2.00_Sn
MAEJ9120_TXL_0.80_Sn
BIN76822_TXL_1.00
BIN76822_THIN_1.50
Below Snap-shot of error:
You will need to split the string on the line break then find the second _ and grab all that comes after it.
=MAX(--MID(TRIM(MID(SUBSTITUTE(A1,CHAR(10),REPT(" ",999)),(ROW($ZZ$1:INDEX($ZZ:$ZZ,LEN(A1)-LEN(SUBSTITUTE(A1,CHAR(10),""))+1))-1)*999+1,999)),FIND("}}}",SUBSTITUTE(TRIM(MID(SUBSTITUTE(A1,CHAR(10),REPT(" ",999)),(ROW($ZZ$1:INDEX($ZZ:$ZZ,LEN(A1)-LEN(SUBSTITUTE(A1,CHAR(10),""))+1))-1)*999+1,999)),"_","}}}",2))+1,999))
This is an array formula and must be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode. If done correctly then Excel will put {} around the formula.
For Minimum replace the MAX for MIN
Following is a VBA based function. You need to insert a module in VBA and paste below code.
Public Function GetMinMax(strInput As String, Optional varMode)
Dim strMode As String
If IsMissing(varMode) Then
strMode = "MAX"
Else
strMode = varMode
End If
Dim varInput
varInput = Split(strInput, Chr(10))
Dim varOut() As Double
Dim i As Long
ReDim varOut(UBound(varInput))
For i = LBound(varInput) To UBound(varInput)
varOut(i) = Split(varInput(i), "_")(2)
Next i
If strMode = "MAX" Then
GetMinMax = Application.Max(varOut)
Else
GetMinMax = Application.Min(varOut)
End If
End Function
To get MAX you can use:
=GetMinMax(A1) or =GetMinMax(A1,"MAX")
For MIN you can use:
=GetMinMax(A1,"MIN")
With Excel 2013+, you can use FILTERXML to split the rows within the cell into an array; and then apply one of the usual methods to examine the rightmost, underscore separated segment as a number:
=MAX(--(RIGHT(SUBSTITUTE(FILTERXML("<t><s>" & SUBSTITUTE(A1,CHAR(10),"</s><s>")&"</s></t>","//s"),"_",REPT(" ",99)),99)))
=MIN(--(RIGHT(SUBSTITUTE(FILTERXML("<t><s>" & SUBSTITUTE(A1,CHAR(10),"</s><s>")&"</s></t>","//s"),"_",REPT(" ",99)),99)))
Or, you can just create an XML with nodes/subnodes at the LF and _ and use the XPATH to return the last element in each substring; then apply MAX and MIN:
= MAX(FILTERXML("<t><s><u>" & SUBSTITUTE(SUBSTITUTE(A1,"_","</u><u>"),CHAR(10),"</u></s><s><u>") & "</u></s></t>","//u[last()]"))
= MIN(FILTERXML("<t><s><u>" & SUBSTITUTE(SUBSTITUTE(A1,"_","</u><u>"),CHAR(10),"</u></s><s><u>") & "</u></s></t>","//u[last()]"))
Another shorter formula option using FILTERXML function, just adopt XPATH to return MAX/MIN value and need not REPT, MAX and MIN function
To get MAX value, you can use:
=FILTERXML("<a><b>"&SUBSTITUTE(SUBSTITUTE(A1,CHAR(10),"_A"),"_","</b><b>")&"</b></a>","//b[not(//b>.)>.*0]")
To get MIN value, you can use:
=FILTERXML("<a><b>"&SUBSTITUTE(SUBSTITUTE(A1,CHAR(10),"_A"),"_","</b><b>")&"</b></a>","//b[not(//b<.)>.*0]")

How to reverse comma separated string in Excel?

How can I use a formula in MS Excel to reverse a comma separated sting in row? The number of values are not always same so some rows have 3, 4 and some only one element.
So the output looks like following image
If you have Office 365 Excel then use This array formula:
=TEXTJOIN(",",,TRIM(MID(SUBSTITUTE(A2,",",REPT(" ",99)),((LEN(A2)-LEN(SUBSTITUTE(A2,",",""))+1)-ROW($XFD$1:INDEX(XFD:XFD,LEN(A2)-LEN(SUBSTITUTE(A2,",",""))+1)))*99+1,99)))
Being an array formula it needs to be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode.
If one does not have Office 365 Excel then vba will probably be the only choice.
Sloppy UDF solution:
Function REVERSESTRING(original As Range, delim As String)
Dim i As Long, reversed As String, arr As Variant
arr = Split(original.Value, delim)
For i = UBound(arr) To 0 Step -1
reversed = reversed & arr(i) & ","
Next i
REVERSESTRING = Left(reversed, Len(reversed) - 1)
End Function
A pure single-cell formula works in Excel 365; with that version there is no need for special array calculation entry, VBscripts, or helper columns:
Procedure
Make the following table in Excel:
Add column headings
Select headings, and press the Table icon on the Insert ribbon tab
Copy formula 1 & 2 below into the appropriate columns
Original | # | Reversed
---------+-----+-----------
| {1} | {2}
Place the string you would like to reverse in the first column.
Details and customization
'Original' column
(holds the delimited string value you wish to process)
Place your source data in this column (as seen in the example images)
'#' column
(counts number of items in delimited cell)
Note: (this column is completely optional; it simply shows the number of items in the original column)
{1} <- replace with formula #1:
=LET(
existingDelimiter, ","
, originalValue, [#[Original]]
, SUM(
LEN(originalValue)
- LEN(
SUBSTITUTE(
originalValue
, existingDelimiter
, ""
)
)
)
+1
)
(Note: If your list uses a delimiter other than a comma or if the first column of your table will have a different name, edit the above as appropriate using the same instructions given for formula #2 below.)
'Reversed' column
(reverses order of delimited items in list; optionally changes delimiter)
{2} <- replace with formula #2:
=LET(
existingDelimiter, ","
, newDelimiter, ","
, originalValue, [#[Original]]
, SUBSTITUTE(
ARRAYTOTEXT(
LET(
list,
IFERROR(
FILTERXML(
"<t><s>"
& SUBSTITUTE(
originalValue
,existingDelimiter
,"</s><s>"
)
& "</s></t>"
, "//s"
)
,""
)
,SORTBY(
list,
SEQUENCE(
ROWS(list)
,1
,ROWS(list)
,-1
)
)
)
,0
)
, ", "
, newDelimiter
)
)
Adjust the formula for what you are trying to accomplish by changing the values for existingDelimiter, newDelimiter, and originalValue, as necessary.
A. To reverse a comma-separated string, use the formula as written:
existingDelimiter, ","
, newDelimiter, ","
Example:
B. To reverse DNS names, replace the comma with a period in the definitions for existingDelimiter and newDelimiter:
existingDelimiter, "."
, newDelimiter, "."
This can be very useful for reverse DNS names (aka Java class names / Universal Type Indicators (UTIs) / etc.)
Example:
Replace "Original" in [#[Original]] with the name of your first column, if different.
A. If using just a single cell for input instead of a table column, replace [#[Original]] with the reference to that cell (e.g. B2):
, originalValue, B2
Example:
Explaination of the "Reversed" column formula:
By manually converting to XML, we can use the FilterXML function, which converts the data to an array.
Having the data in an array allows the use of the SortBy function.
SortBy reverses the array by using a helper array created with the Sequence function.
Finally, the ArrayToText functions converts this (now reverse-ordered) array back to a text string that will fit in a single spreadsheet cell.
This is what allows us not to need a loop, helper columns, or VBScript.
Bonus column
To extract a specific term from a list, use the following formula in another table column:
(Change the number in termNumber to the desired value):
=LET(
existingDelimiter, ","
, originalValue, [#[Original]]
, termNumber, "[2]"
, IFERROR(
FILTERXML(
"<t><s>"
& SUBSTITUTE(
originalValue
, existingDelimiter
, "</s><s>"
)
& "</s></t>"
, "//s"
& termNumber
)
, ""
)
)
Example:
Other notes
Needed:
Excel 365 (for at least FilterXML and Let functions, and dynamic arrays) (*)
It might work with other versions but I have not tested those. Please note in the comments if you notice other or future versions (e.g. Excel 2022) work with this.
Not needed:
dynamic array entry
VBscript
macro-enabled files
(*) This can be done without the Let function, but using Let allows the calculation to be edited / repurposed with less chance for user error.
Note: When the Lambda function is released (hopefully later in 2021) then this all can be wrapped up in a named worksheet function
Bonus: to edit more complex Excel formulas with code highlighting (and other features such as auto-intents and folding), try a text editor with Swift programming language support. Examples:
VSCode editor (free; cross-platform)
Until there is an Excel-specific extension available, install a Swift VSCode extension such as this: Swift language VSCode extension (it seems to work quite well to provide code highlighting and folding)
Notepad++ (free; Windows-only)
Select "Swift" from the "Language" menu
Inspired by (apologies if I've forgotten someone):
https://www.howtoexcel.org/tutorials/split-text-by-delimiter/
https://www.sumproduct.com/news/article/have-you-got-what-it-text-introducing-arraytotext-and-valuetotext
https://exceljet.net/formula/reverse-a-list-or-range

Split Cell by Numbers Within Cell

I have some fields that need to be split up into different cells. They are in the following format:
Numbers on Mission 21 0 21
Numbers on Mission 5 1 6
The desired output would be 4 separate cells. The first would contain the words in the string "Numbers on Mission" and the subsequent cells would have each number, which is determined by a space. So for the first example the numbers to extract would be 21, 0, 21. Each would be in its own cell next to the string value. And for the second: 5, 1, 6.
I tried using a split function but wasn't sure how to target the numbers specifically, and to identify the numbers based on the spaces separating them.
Pertinent to your first case (Numbers on Mission), the simple solution could be as shown below:
Sub SplitCells()
Const RowHeader As String = "Numbers on Mission"
Dim ArrNum As Variant
ArrNum = Split(Replace(Range("A1"), RowHeader, ""), " ")
For i = 1 To UBound(ArrNum)
Cells(1, i + 2) = ArrNum(i)
Next
Cells(1, 2) = RowHeader
End Sub
The same logic is applicable to your second case. Hope this may help.
Unless I'm overlooking something, you may not need VBA at all. Have you tried the "Text to Columns" option? If you select the cell(s) with the information you would like to split up, and go to Data -> Text to Columns. There, you can choose "delimited" and choose a space as a delimiter, which will split your data into multiple cells, split by where the space is.
edit: Just realized that will also split up your string. In that case, when you are in 3rd part of the Text to Columns, choose a destaination cell that isn't the cell with your data. (I.E. if your data is in A1, choose B1 as destination, and it'll put the split info there. Then just combine the text columns with something like =B1&" "&C1&" "&D1)
I was able to properly split the values using the following:
If i.Value Like "*on Mission*" Then
x = Split(i, " ")
For y = 0 To UBound(x)
i.Offset(0, y + 1).Value = x(y)
Next y
End If

Deleting variable number of leading characters from a variable-length string

If I am having G4ED7883666 and I want the output to be 7883666
and I have to apply this on a range of cells and they are not the same length and the only common thing is that I have to delete anything before the number that lies before the alphabet?
This formula finds the last number in a string, that is, all digits to the right of the last alpha character in the string.
=RIGHT(A1,MATCH(99,IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99),0)-1)
Note that this is an array formula and must be entered with the Control-Shift-Enter keyboard combination.
How the formula works
Let's assume that the target string is fairly simple: "G4E78"
Working outward from the middle of the formula, the first thing to do is create an array with the elements 1 through 25. (Although this might seem to limit the formula to strings with no more than 25 characters, it actually places a limit of 25 digits on the size of the number that may be extracted by the formula.
ROW($1:$25) = {1;2;3;4;5;6;7; etc.}
Subtracting from this array the value of (1 + the length of the target string) produces a new array, the elements of which count down from the length of string. The first five elements will correspond to the position of the characters of the string - in reverse order!
LEN(A1)+1-ROW($1:$25) = {5;4;3;2;1;0;-1;-2;-3;-4; etc.}
The MID function then creates a new array that reverses the order of the characters of the string.
For example, the first element of the new array is the result of MID(A1, 5, 1), the second of MID(A1, 4, 1) and so on. The #VALUE! errors reflect the fact that MID cannot evaluate 0 or negative values as the position of a string, e.g., MID(A1,0,1) = #VALUE!.
MID(A1,LEN(A1)+1-ROW($1:$25),1) = {"8";"7";"E";"4";"G";#VALUE!;#VALUE!; etc.}
Multiplying the elements of the array by 1 turns the character elements of that array to #VALUE! errors as well.
=1*MID(A1,LEN(A1)+1-ROW($1:$25),1) = {"8";"7";#VALUE!;"4";#VALUE!;#VALUE!;#VALUE!; etc.}
And the IFERROR function turns the #VALUES into 99, which is just an arbitrary number greater than the value of a single digit.
IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99) = {8;7;99;4;99;99;99; etc.}
Matching on the 99 gives the position of the first non-digit character counting from the right end of the string. In this case, "E" is the first non-digit in the reversed string "87E4G", at position 3. This is equivalent to saying that the number we are looking for at the end of the string, plus the "E", is 3 characters long.
MATCH(99,IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99),0) = 3
So, for the final step, we take 3 - 1 (for the "E) characters from the right of string.
RIGHT(A1,MATCH(99,IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99),0)-1) = "78"
One more submission for you to consider. This VBA function will get the right most digits before the first non-numeric character
Public Function GetRightNumbers(str As String)
Dim i As Integer
For i = Len(str) To 0 Step -1
If Not IsNumeric(Mid(str, i, 1)) Then
Exit For
End If
Next i
GetRightNumbers = Mid(str, i + 1)
End Function
You can write some VBA to format the data (just starting at the end and working back until you hit a non-number.)
Or you could (if you're happy to get an addin like Excelicious) then you can use regular expressions to format the text via a formula. An expression like [0-9]+$ would return all the numbers at the end of a string IIRC.
NOTE: This uses the regex pattern in James Snell's answer, so please upvote his answer if you find this useful.
Your best bet is to use a regular expression. You need to set a reference to VBScript Regular Expressions for this to work. Tools --> References...
Now you can use regex in your VBA.
This will find the numbers at the end of each cell. I am placing the result next to the original so that you can verify it is working the way you want. You can modify it to replace the cell as soon as you feel comfortable with it. The code works regardless of the length of the string you are evaluating, and will skip the cell if it doesn't find a match.
Sub GetTrailingNumbers()
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Dim result As Object, results As Object
Dim regEx As New VBScript_RegExp_55.RegExp
Set ws = ThisWorkbook.Sheets("Sheet1")
' range is hard-coded here, but you can define
' it programatically based on the shape of your data
Set rng = ws.Range("A1:A3")
' pattern from James Snell's answer
regEx.Pattern = "[0-9]+$"
For Each cell In rng
If regEx.Test(cell.Value) Then
Set results = regEx.Execute(cell.Value)
For Each result In results
cell.Offset(, 1).Value = result.Value
Next result
End If
Next cell
End Sub
Takes the first 4 digits from the right of num:
num1=Right(num,4)
Takes the first 5 digits from the left of num:
num1=Left(num,5)
First takes the first ten digits from the left then takes the first four digits from the right:
num1=Right(Left(num, 10),4)
In your case:
num=G4ED7883666
num1=Right(num,7)

Resources