How to reverse comma separated string in Excel? - 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

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 2013 - Comparing Two Cells

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.

How/which formula to use, to show combine text results for false condition (for pending task reporting usage)?

Wanted to check if CONCATENATE is the one to use (not sure if my excel has TEXTJOIN), and how to show just the text that has empty value in the cells.
For example in my attachment below, I want the intended result shown like in B2 and B3, where the texts shown with delimiter, when the values are false (empty).
If I were to use CONCATENATE like in Row 10 and Row 11, it's rather manual and it only capture "positive values" as in non-blank cells.
Purpose: To show pending tasks (empty/blank status cells)
Use MID with CONCATENATED IFS:
=MID(IF(C2="","/"&$C$1,"")&IF(D2="","/"&$D$1,"")&IF(E2="","/"&$E$1,"")&IF(F2="","/"&$F$1,"")&IF(GC2="","/"&$G$1,"")&IF(H2="","/"&$H$1,""),2,999)
I would use TEXJOIN and FILTER if you have the newest version of Excel.
For example: =TEXTJOIN("/",1,FILTER($E$2:$I$2, ISBLANK(E3:I3)))
EDIT: For older versions, a temporary workaround is as follows:
make a temporary array the same size as your original dataframe where each value is determined by a formula such as =IF(ISBLANK(E3), E$2&"/","")
Use something like =LEFT(CONCAT(E15:J15), LEN(CONCAT(E15:J15))-1) to get the desired result (where E15:J15 is where I elected to store the first row of the temporary array created in step 1).
I am not sure of your Excel version, but I think this would work in older versions (formatted for readability - will work if you paste it directly into cell B2 and copy down):
=LEFT(CONCAT( INDEX( CHOOSE({1;2;3},$C$1:$H$1,{"/","/","/","/","/","/"},{"","","","","",""}),
INDEX( IF(ISBLANK(C2:H2),{1;2},{3;3}),
MOD(COLUMN(A1:INDEX(1:1,,12))-1,2)+1,
(COLUMN(A1:INDEX(1:1,,12))-1)/2+1 ),
(COLUMN(A1:INDEX(1:1,,12))-1)/2+1 ) ),
SUM(7*ISBLANK(C2:H2))-1 )
Notes
As this is an array formula, you may have to enter it with CTRL + SHIFT + ENTER with an older version of Excel.
The stat labels must all have a length of 6 characters as shown in your post. If not, then they must at least have the same length and the last line SUM(7*ISBLANK(C2:H2))-1 must be changed to replace the 7 with the string length + 1, e.g. a length of 9 would be SUM(10*ISBLANK(C2:H2))-1.
If they don't have the same length, the LEFT( can be removed along with the SUM(10*ISBLANK(C2:H2))-1) at the end. You will end up having a trailing / delimiter at the end. You could fix that for the case of stat F being the last part by changing {"/","/","/","/","/","/"} to {"/","/","/","/","/",""}, but the other cases would still have a trailing /. Another approach is much more complex, but the component SUM(10*ISBLANK(C2:H2))-1) could be shaped to identify what to cut off or maybe a helper column could be built - in any case, let's hope your situation is that the stat labels all have the same length.
The delimiter "/" can be changed, but must always be a single character. If not, then then last line must be changed to SUM( [label length + delimiter length] *ISBLANK(C2:H2))-1.
This formula is fixed to 6 stat columns. If you need for it to accommodate more, it is possible by extending the {"/","/","/","/","/","/"} and {"","","","","",""} (one element for each new column) and replacing every 12 with 2 times the number of columns. Also, obviously, the references $C$1:$H$1 and C1:H2 must be changed to read in your new columns.

Excel - Is there a way to tell a cell to use a certain function based on the first letter of a another cell?

I am creating a Product Decoder for a project.
Lets say, Our product can have a code such as "ABCDE" OR a code like "BCDEF".
ABCDE has a table of data that I use to decode using a lookup. For example AB can decode into "Potato" and CDE can decode into "Chip". So any combination with AB can be Potato "Anything else".
BCDER, BC can decode into "Veggie" so DER can code into "Chip".
I also use the 1/search method to take placements for the decode. Example =IFERROR(LOOKUP(2,1/SEARCH($E$19:$E$23,N18),$E$19:$E$23), "")
I concatenate all the decodes using =S2&" "&T2&" "&U2&" "&V2
Question is...if we are getting a huge amount of product code coming that I want to decode into one single column... How do I tell excel to use this table of data for ABCDE if product starts with "A", if not, use table of data that correlates to BCDER when product starts with "B".
Edit 1:
Here is my table, right side is where i look up the Part Number column N"
As you can see on column "W" I concatenate the date is Look up from columns O~V.
Column O Function: =IFERROR(LOOKUP(2,1/SEARCH($C$1:$C$7,N2),$C$1:$C$7), "")
On column N, I have two parts. One that starts with M and one that starts with K which is pretty standard.
Image two is me trying to use the IF Left but, it doesn't really work
=IF(LEFT(AA4,10) = "M ", W2, W18)
So How can I tell my excel page to use Table A1:A12 if part starts with "M*" and vice versa?
Let me know if this is confusing, I will do my best to clear things up.
First, a possible correction
I think this function does not give you what you say it does:
= IFERROR(LOOKUP(2,1/SEARCH($E$19:$E$23,N18),$E$19:$E$23), "")
You might mean:
= IFERROR( LOOKUP( 2, 1/SEARCH( $E$19:$E$23, N18 ), $F$19:$F$23 ), "" )
Because you want to look up the value in column E and return the value in column F. If that's not true, then skip the rest of this answer.
Now the solution
What you're trying to do is change the lookup array if the part number starts with a different letter. So, the IF( LEFT( combo mentioned by #BigBen should be used to modify the lookup array. I think it would look like this:
= IFERROR( LOOKUP( 2
,1/SEARCH( IF( LEFT( AA4, 1 ) = "M"
,$C$2:$C$12
,$C$19:$C$23 )
,N2 )
,IF( LEFT( AA4, 1 ) = "M"
,$D$2:$D$12
,$D$19:$D$23 )
)
,"")

String comparison between two cells containing text but one containing multiple delimiters

I wish to compare two cells in excel which contain similar text but one containing different delimiters in it. I want to ignore the delimiters while comparing the strings.
Eg.
John Doe: Mary Ann. Are Married/
John Doe Mary Ann Are Married
I am totally unaware of macros. Any leads are appreciated!
If you have Office 365 Excel then we can use this array formula:
=TEXTJOIN("",TRUE,IF(((CODE(UPPER(MID(A1,ROW(INDIRECT("1:" & LEN(A1))),1)))>=65)*(CODE(UPPER(MID(A1,ROW(INDIRECT("1:" & LEN(A1))),1)))<=90))+(CODE(UPPER(MID(A1,ROW(INDIRECT("1:" & LEN(A1))),1)))=32),MID(A1,ROW(INDIRECT("1:" & LEN(A1))),1),""))=TEXTJOIN("",TRUE,IF(((CODE(UPPER(MID(A2,ROW(INDIRECT("1:" & LEN(A2))),1)))>=65)*(CODE(UPPER(MID(A2,ROW(INDIRECT("1:" & LEN(A2))),1)))<=90))+(CODE(UPPER(MID(A2,ROW(INDIRECT("1:" & LEN(A2))),1)))=32),MID(A2,ROW(INDIRECT("1:" & LEN(A2))),1),""))
Being an array formula it must be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode. If done correctly then Excel will put {} around the formula.
You can try this:
Function CompareByLetter(t1 As String, t2 As String) As Boolean
CompareByLetter = CleanString(t1) = CleanString(t2)
End Function
Function CleanString(t As String) As String
Dim t1, x, c
For x = 1 To Len(t)
c = Asc(UCase(Mid(t, x, 1)))
If (c >= 65 And c <= 90) Or c = 32 Then t1 = t1 & Mid(t, x, 1)
Next x
CleanString = t1
End Function
Then you can use it as a formula:
=CompareByLetter(A1,A2)
This macro just compares strings by only keeping letters and spaces.
You can also use =CleanString(A1) to remove all other characters from your strings.
To use this in your project, open excel and press ALT+F11
Right click off to the left side and select Insert -> Module
Paste the code into the module window on the right (see attached).
Image mirror since Stackoverflow image hosting seems down
After that, you should be able to use the functions as you would a formula.
Just enter =CompareByLetter(A1,A2) in a cell.

Resources