Problem: I need a formula to automatically increment when dragging down. Since the 3 strings are joined by concatenating, it doesn't seem to work.
=CONCATENATE("Sheet2!", SUBSTITUTE(ADDRESS(1,MATCH("String to Search For", Sheet2!$13:$13,0),4),1,""),"17")
String 1 is a separate sheet reference (Sheet2!)
String 2 is the column number converted to column letter where string to be searched is found using MATCH, ADDRESS, and SUBSTITUTE. In this case it was column 2 converted to B.
String 3 is the row number that I need information from IF string searched is found
After concatenating these, I need to drag it down 5000 rows and increment String 3 (the row number) but because the reference is concatenated, it will not increment. I've tried everything! Please help!
Try adding a ROWS function, e.g. if you put the first formula in Z2 use this version copied down
=CONCATENATE("Sheet2!", SUBSTITUTE(ADDRESS(1,MATCH("String to Search For", Sheet2!$13:$13,0),4),1,""),"17"+ROWS(Z$2:Z2)-1)
change depending on actual start cell
ROWS function, used this way, will increment by 1 each row and is more "robust" than alternatives using ROW for example
Assuming your formula is in row 2, you could do this:
=CONCATENATE("Sheet2!", SUBSTITUTE(ADDRESS(1,MATCH("String to Search For", Sheet2!$13:$13,0),4),1,""),text(row()+15,"#"))
If your formulas starts on a different row, just change the 15 as needed.
This was taken and improved slightly from Question that has since been deleted
For those who can see deleted posts, it was taken from here: https://stackoverflow.com/questions/39793322/three-dimensional-lookup-no-concatenate-or-named-ranges-excel
I'm trying to do a three dimensional lookup without named ranges or concatenates. Simplified, my data is on the form:
Column1 Column2 Column3
Scott
P 1 2 3
M 4 5 6
N 7 8 9
George
P 10 11 12
M 13 14 15
N 16 17 18
I now want to search for a specific Name and then for a specific letter within that names table, I then want to match this row number with a specific column.
I tried a simple INDEX/MATCH:
=INDEX(A:D,MATCH("M",A:A,0),MATCH("Column1",1:1,0))
And that works for the fist name but not any others as it finds the first instance of M.
How do I modify it to look for a different name?
I have answered below, but want to see if someone has a better solution.
I used an IF() statement array formula to find what the P row number was after the George row... I also needed to use the MIN() function to get the first P row number after the name.
Beyond that, it's a simple INDEX() function.... that racked my brain for over an hour :).
=INDEX($A$1:$D$9,MIN(IF((ROW(A1:A9)>MATCH($F$4,A1:A9,0))*(A1:A9=$F$5),ROW(A1:A9),"")),MATCH($F$6,$A$1:$D$1,0))
Don't Forget!
Use Ctrl+Shift+Enter when finishing the formula, so it gets evaluated as an array formula.
You can use two other INDEX/MATCH's inside the first MATCH to set the lookup range. Then you simply need to add the MATCH() to find the absolute position of the name.
=INDEX(A:D,MATCH($H$4,INDEX(A:A,MATCH($H$3,A:A,0)):INDEX(A:A,MATCH($H$3,A:A,0)+4),0)+MATCH($H$3,A:A,0)-1,MATCH($H$5,$1:$1,0))
This one works better and does not have a size constraint:
=INDEX(A:D,MATCH(F4,INDEX(A:A,MATCH(F3,A:A,0)):A1040000,0)+MATCH(F3,A:A,0)-1,MATCH(F5,A1:D1,0))
You can do this just by adding the results of two matches together. One match for the names plus one match for the letter equals the total row.
=INDEX(A:D,MATCH(G5,A3:A5,0)+MATCH(G3,A:A,0),MATCH(G4,1:1,0))
In other words: Index(All of the Data, Match(Name, In name column, exact) + Match(Letter, In letter column, exact), Match(Column name, in Column row, exact)
Screen capture of working sheet
My answer attempts the general case with only one caveat:
That a letter is single character text, and a name is more than 1 character. Otherwise i feel there is no difference logically between letters and names, and it is then impossible to really do...
RE-EDIT for better function construction:
{=INDEX($A$1:$D$17, MATCH($H$3,$A1:$A17, 0)+MATCH($H$4, INDEX($A1:$A17, MATCH($H$3,$A1:$A17, 0)):INDEX($A:$A, SMALL(IFERROR(MATCH($H$3,$A1:$A17, 0)+POWER(SQRT(IF(LEN($A$1:$A$17)>1, ROW($A$1:$A$17), 0)-MATCH($H$3,$A$1:$A$17, 0)), 2)-1, ROWS($A$1:$A$17)), 2)), 0)-1, MATCH($H$5, $A$1:$D$1, 0))}
This uses an array formula along column A, and checks if the length is > 1 and throws the row nums into an array, with letters given a 0.
Then match row of unique name(e.g. George) is subtracted from each.
We then use a min(of all other name rows, with the last data row as the final default - SMALL function with 2 parameter) to find the next name row(or last data row if there is no following name).
Rest is standard index/match etc.
It will correctly return #N/A if there is no such letter under the chosen name...
My dataset is A1:A17, and the formula could use A:A instead each time, but the array calc inside the IF needs the A1:A17 for speed.
EDIT for better function construction:
If we wanted to avoid editing the formula when the data length changes, then we could let full column references of A:A go through the entire construction(and lose speed/efficiency) with the last data row in colA calculated via ROWS(A:A):
Re-edit:
{=INDEX($A:$D, MATCH($H$3,$A:$A, 0)+MATCH($H$4, INDEX($A:$A, MATCH($H$3,$A:$A, 0)):INDEX($A:$A, SMALL(IFERROR(MATCH($H$3,$A:$A, 0)+POWER(SQRT(IF(LEN($A:$A)>1, ROW($A:$A), 0)-MATCH($H$3,$A:$A, 0)), 2)-1, ROWS($A:$A)), 2)), 0)-1, MATCH($H$5,1:1, 0))}
It really depends on the setup...
Edit again for version which takes blanks as separators for names
If you want to use blanks as the separator for names, where no blanks are in the data results, but blanks appear in columns B to D where there is a name, then a tiny change in the above formulae will result in this:
=INDEX($A$1:$D$17, MATCH($H$3,$A$1:$A$17, 0)+MATCH($H$4, INDEX($A:$A, MATCH($H$3,$A:$A, 0)):INDEX($A:$A, SMALL(IFERROR(MATCH($H$3,$A:$A, 0)+POWER(SQRT(IF($B$1:$B$17="", ROW($A$1:$A$17), 0)-MATCH($H$3,$A$1:$A$17, 0)), 2)-1, ROWS($A$1:$A$17)), 2)), 0)-1, MATCH($H$5, $A$1:$D$1, 0))
This means that the names and letters do not have to be any specified length, but just one proviso is that blanks appear in the row with the name.
A small amendment to the condition to find the end range to search for the letter by replacing this: SQRT(IF(LEN($A$1:$A$17)>1, with this:
SQRT(IF($B$1:$B$17="",
I would use the area (4th parameter) of Index(). Below is a screenshot of test data. This example assumes the same columns and keys are sorted and consistent.
This works by using (Range1,Range2) as the first parameter of index. For the 4th parameter of index, use N for which area in the () you want Index to return.
I think this may be slightly tidier, and a little easier to modify maybe.
=INDEX(OFFSET(INDIRECT("A"&MATCH($H$3,$A:$A,0),TRUE),0,0,4,4),MATCH($H$4,$A:$A,0),MATCH(H5,$1:$1,0))
Using offset to create the range first, we're able to use the name from H3 to set that up, and then beyond that we are just indexing within that new range.
Now this is still dependendent on staying in Column A for the names.
Assuming the format of the data is always Name then P, M and N this formula does the work:
=INDEX($A:$D,
MATCH($H$3,$A:$A,0)
+LOOKUP($H$4,{"P",1;"M",2;"N",3}),
MATCH($H$5,$1:$1,0))
This solution works on almost all conditions. One restriction I found is when one of the subjects (Names) does no have data for any of the details (letters), but as of now the same occurs with all the other answers.
The formula assumes the data is located at B6:F30 (in order to ensure it can be applied regardless of the source range location).
The formula uses the Index\Match functions:
First, a MATCH to retrieve the position of the Name:
MATCH($H8,$B$6:$B$30,0)
With that info it uses INDEX to build a range that is used to obtain the position of the Detail (letter) using a second MATCH Function:
+ MATCH($I8,INDEX($B$6:$B$30, 1 + MATCH($H8,$B$6:$B$30,0))
:INDEX($B$6:$B$30,ROWS($B$6:$B$30)),0),
Adding the results of the first and second MATCH functions obtains the position of the Name`Detail` combination and uses it in an Index to the entire data. The position of the Data Column required is obtained with a Match:
INDEX($B$6:$F$30, 1st.MATCH + 2nd.MATCH,
MATCH(J$6,$B$6:$F$6,0))
With the results located at G6:L30 enter this formula in J8 then copy to J8:L30:
= INDEX( $B$6:$F$30,
MATCH( $H8, $B$6:$B$30, 0)
+MATCH( $I8, INDEX( $B$6:$B$30 , 1 + MATCH( $H8, $B$6:$B$30 ,0))
: INDEX( $B$6:$B$30, ROWS($B$6:$B$30) ),0),
MATCH( J$6, $B$6:$F$6, 0)),"")
This solution works in all conditions discussed so far (let me know of any condition that it does not work and I’ll try to cover it).
I’m posting this as a separated answer as the formulas applied in prior answer rightly apply to the conditions stated in them, as such they will be useful to users with those specific scenarios, so they don’t need to apply these long formulas.
This formula assumes the data is located at B6:E30 (in order to ensure it can be applied regardless of the source range location).
This formula uses the Index\Match functions and it’s a Formula Array.
FormulaArrays are entered pressing [Ctrl] + [Shift] + [Enter] simultaneously, you shall see { and } around the formula if entered correctly
Syntax:
=IFERROR(INDEX(DataRng,
MATCH(Value1,NamesRng,0)
+IFERROR(MATCH(Value2,INDEX(NamesRng,
1+MATCH(Value1,NamesRng,0))
:INDEX(NamesRng, IFERROR(MATCH(Value1,NamesRng,0)
+MATCH("#",IF((INDEX(Col1Rng,1+MATCH(Value1,NamesRng,0))
:INDEX(Col1Rng,ROWS(NamesRng)))="","#","!"),0),
ROWS(NamesRng))),0),NA()),MATCH(ValCol,DataHdr,0)),"")
Arguments:
Assuming the data is located at B6:E30.
Value1= Name to be found in Data, i.e. George, Scott, etc.
Value2= Detail to be found in Data, i.e. Detail1, Detalle2, etc.
ValCol = Column to be found in Data i.e. Column1, Column2, etc.
DataRng= $B$6:$E$30
DataHdr= $B$6:$E$6
NamesRng= $B$6:$B$30
Col1Rng= $C$6:$C$30
1st MATCH: Retrieves the position of the Name:
MATCH(Value1,NamesRng,0)
2nd MATCH: Retrieves the end position of the Name’s corresponding Details, which is determined by a blank value in column C or the end of the data range:
MATCH("#",IF((INDEX(Col1Rng, 1 + 1stMATCH)
:INDEX(Col1Rng,ROWS(NamesRng)))="","#","!"),0),
Builds a Range (vRange): With the Names's Details using the 1st and 2nd match functions. If 2nd Match returns an error then it uses the last row of the Data range:
INDEX(NamesRng, 1 + 1stMATCH )
:INDEX(NamesRng, IFERROR( 1stMATCH + 2ndMATCH, ROWS(NamesRng)))
3rd MATCH: Retrieves the position of the Detail within the vRange. It returns #NA if the combination is not present.
IFERROR(MATCH(Value2, vRange,0), NA())
Adding the results of the 1st and 3rd match functions obtains the Row index of the Name`Detailcombination or#NAif no found.
The Column index is obtained with a Match from the Header of the Data.
It then applying the INDEX function to the Data Range returns the value of theName\Detail\Columncombination.
If theName\Detail` combination is not found it returns blank.
=IFERROR( INDEX( DataRng, 1stMATCH + 3rdMATCH, MATCH(Column,DataHdr,0)),"")
With the results located at H6:L37 enter this Formula Array in J8 then copy to K8:L37 and to J9:L37:
=IFERROR( INDEX($B$6:$E$30,
MATCH($H8,$B$6:$B$30,0)
+IFERROR( MATCH($I8, INDEX($B$6:$B$30,
1+MATCH($H8,$B$6:$B$30,0))
:INDEX($B$6:$B$30, IFERROR(MATCH($H8,$B$6:$B$30,0)
+MATCH("#", IF((INDEX($C$6:$C$30,1+MATCH($H8,$B$6:$B$30,0))
:INDEX($C$6:$C$30,ROWS($B$6:$B$30)))="","#","!"),0),
ROWS($B$6:$B$30))),0),NA()),
MATCH(J$6,$B$6:$E$6,0)), "")
Wow... So many solutions already.
I think a simpler solution could be using offset to get a more generic answer.
=INDEX($A$1:$D$9, MATCH($G$3,OFFSET($A$1,MATCH($G$2,$A$1:$A$9,0),0,3,1),0)+MATCH($G$2,$A$1:$A$9,0), MATCH($G$4,$B$1:$D$1,0)+1)
The only variable to look for is 3 which is the number of M/N/P options present because that will affect the number of rows. Otherwise, the solution works fine in all possible scenarios and different orders.
When I have more than two inpunts for a data search I prefer to have the data organized as shown in the figure, so that I can use a pivot table and get it to organize the data in rows and columns as I like.
Then I use GETPIVOTDATA to search for a value.
Cell G9 contains this formula:
=GETPIVOTDATA("Value";$F$3;"Name";G15;"Letter";G16;"Column";G17)
Say, I got 2 words A1:ddC, A2:DDC
I want to convert these 2 words into a unique code so that so that i can do the Case Sensitive Vlookup.
So i tried, =Code(A1) & it returned 100, but if i tried =Code("dady") then it also returns 100. It is cos =Code() only pic the first char of the word.
I want to convert a word to a Unique Code (could be ASCII code or any form of unique code).
So how to do that without using VBA?
As this is a hash, it would be possible for some strings to end up with the same value, but it would be unlikely.
Note that the row function uses 1:255 to generate a list of numbers from 1 to 255 - change this number if your strings end up longer.
=A1&SUMPRODUCT(IF(IFERROR(CODE(MID(A1,ROW($1:$255),1)),0)>96,1,0),POWER(2,ROW($1:$255)))
This has to be entered as an array formula using CTRL+SHIFT+ENTER - you will see {} around the formula if you have successfully done that.
This will produce a decimal representation of the upper and lower case letters, and this is then appended to the word itself - this will guarantee uniqueness, as the only way to have a word and number match is to have the same word and case, which means it was a duplicate in the first place.
With this, ddC = ddC & 1*2 + 1*4 + 0*8 = ddC6
DDC = DDC & 0*2 + 0*4 + 0*6 = DDC0
ddC (ddC with a space after it) = ddc & 1*2 + 1*4 + 1*8 + 0*16 = ddC 6
*WARNING: * This is not a solution to the titled question
"How to convert a word to a Unique Code in Excel using Formula without using VBA?" but instead is a solution to what I believe is the underling problem as the original question states "so that i can do the Case Sensitive Vlookup." this is a solution acomplishing a Case Sensitive Vlookup, without the need to convert the values before doing so.
An alternative to converting all the values then doing a look up on the converted values, you could use the INDEX and MATCH functions in an array entered formula and directly look up the values:
=INDEX(A1:A14,MATCH(TRUE,EXACT(A1:A14,"ddC"),0))
This will return the value in A1:A14, at the same index of an exact (case-sensitive) match in A1:A14 to ddC you can VERY easily modify this into a look up of other columns.
Explanation:
Start with getting an array of all exact matches in your look up list to your look up value:
So if I enter this formula:
=EXACT(A1:A14,"ddC")
Then go into the formula bar and press F9 it will show me an array of true false values, relating to each cell in the range A1:A14 that are an Exact match to my expression "ddC":
now if we take this Boolean Array, and use the Match function to return the relative position of True in the array.
=MATCH(TRUE,EXACT(A1:A14,"ddC"),0)
But remember we need to enter this by pressing Ctrl + Shift + Enter because we need the EXACT(A1:A14,"ddC") portion of the formula to be returned as an array.
Now that we have the position of the True in the array, in this case 6 we can use that to retrieve the corresponding value in any column, as long as it is relational and that same size. So if we want to return the value of the exact match (although relatively useless in this situation, but will continue for demonstration) in the original look up column we just wrap the last formula up in an Index function:
=INDEX(A1:A14,MATCH(TRUE,EXACT(A1:A14,"ddC"),0))
But remember we need to enter this by pressing Ctrl + Shift + Enter because we need the EXACT(A1:A14,"ddC") portion of the formula to be returned as an array.
Now we can apply that same concept to a larger range for more useful look up function:
But remember we need to enter this by pressing Ctrl + Shift + Enter because we need the EXACT(A1:A14,"ddC") portion of the formula to be returned as an array.
Now notice in this last step I offered 2 formulas:
=INDEX(A1:B14,MATCH(TRUE,EXACT(A1:A14,D2),0),2)
And
=INDEX(B1:B14,MATCH(TRUE,EXACT(A1:A14,D2),0))
The first returns the value in the range A1:B14 in the Second column at the position of the exact match in A1:A14 to the value in D2 (in this case "dady")
The second returns the value in the range B1:B14 at the position of the exact match in A1:A14 to the value in D2 (in this case "dady")
Hopefully someone else can add more input but as far as I know the second might be better performing, as it has a smaller index range, and doesn't require going to the specified column, it is also shorter.
While the first to me is much easier to read, to some (more of a preference I think) because you know that your looking at a look up table that spans 2 columns and that you are returning the value in the second column.
*Notes: * I am note sure if this solution will be better in practice then converting the original values in the first place, seeing as how converting all the values once, then hard coding the converted values will require no additional formula or calculation (if formulas are afterwards replaced with values) once finished, while this method will recalculate, and also is array entered. But I feel in the case the asker is doing a single look up against a changing look up list (one that constantly requires all values are converted at all times using array formula) this option does allow you to remove the formula per word, with one single formula
all in all I hope this solves your original problem,
Cheers!!
if all your strings like the one you pointed above try something like this:
= CONCATENATE(Code(A1) , Code(Mid(A1,2,1)) , Code(Mid(A1,3,1)))
In order to account for capital letters you're going to end up with a VERY long formula, especially if you have long word entries. Without VBA I would approach it this way and set up the formula once to allow for the biggest word you anticipate, and then copy it around as needed.
Formula (to expand):
=CONCATENATE(IF(EXACT(A1,UPPER(A1))=TRUE,"b","s")&CODE(A1),IF(EXACT(A1,UPPER(A1))=TRUE,"b","s")&CODE(MID(A1,2,1)),IF(EXACT(A1,UPPER(A1))=TRUE,"b","s")&CODE(MID(A1,3,1)), . . . )
You can substitue the "b" and "s" with whatever you like. I was just using those for a case check for capital versus lowercase letters (b=big, s=small) and building that into your unique code.
In order to expand this, add additional cases to account for the length of the words you are using by adding this snippet JUST inside the last parenthesis and modifying the "3" in the MID() function to account for a word length of "4", "5", "6", etc.:
IF(EXACT(A1,UPPER(A1))=TRUE,"b","s")&CODE(MID(A1,3,1))
Painful, yes, but it should work.
Cheers.