Name Day 1 Day2 Day 3
John 3 2
John 2 1 4
Using a double Xlookup, when I'm searching for John and Day 2, I cannot get the value 1 and I'm trying Index/Xmatch/xmatch to return me 1 but no luck. Any idea to go about it?
###Updated example picture here###
enter image description here
This is my current formula
=XLOOKUP("John",$A$2:$A$3,XLOOKUP("Day 2",$B$1:$D$1,$B$2:$D$3),,2)
Note that your comments show a different case (wildcard search in names) than your question.
The following formula will result in the first found value of the wildcard search for names starting with the search string in F2 and matching the date in F3:
=LET(range,A1:D3,
c,1-COLUMNS(range),
r,1-ROWS(range),
days,TAKE(range,1,c),
names,TAKE(range,r,1),
data,TAKE(range,r,c),
x,FILTER(data,days=F3),
TAKE(
FILTER(x,
(ISNUMBER(XMATCH(F2&"*",names,2)))*
(x<>"")),
1))
You cannot use XLOOKUP two-way exact match, because it returns the first match and you have duplicated values in column Name.
This solution returns the first non blank value from the input data based on the lookup values. In cell H2 put the following formula:
=LET(colIdx, XMATCH(G3,A1:D1), tb, A2:D5, lkCol, INDEX(tb,, colIdx),
INDEX(TAKE(FILTER(tb, (A2:A5=G2) * (lkCol<>0), "Not Found"),1),colidx)
)
and here is the output:
We use LET for easier reading and composition. First we need to identify the column of our interest. The name colIdx, has the column index. Notice in order to deal with only one data range (tb), I search in all column names including Name. Now we need to identify the corresponding column values. The name lkCol represents that.
Now we have all the elements we need to filter and select the information we are looking for:
FILTER(tb, (A2:A5=G2) * (lkCol<>0), "Not Found")
The output will be the rows from tb for name G2 and filtered for non empty rows from G3 column name. The output has all the columns, now we need to select the column of our interest and only the first non empty value. TAKE extracts only the first row and INDEX the corresponding column.
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)
I am having a really tough time finding a way to pull data from one sheet to another for a specific task.
On sheet1 I have 42005 (jan 1, 2015) in A1 and it goes to 42735 (Dec 31, 2016)
------A1------|42005|42006|42007|42008|...
Employee 1|-Yes--|-Yes--|-Yes--|-Yes-|...
Employee 1|-Yes--|-Yes--|-Yes--|-Yes-|...
I Need a "Yes", "No", or "Different Unique ID" to show up in the above list in the second row for conditional formatting from the data below. If the date in the header is listed in the bottom table, then i need a unique ID.
On sheet2 I have date ranges that employees work (45 days at work and 7 days off, for example)
Employee 1 | Employee 1 0.01| Employee 2 | Employee 2 0.01
Start--------- | End-----------------|Start-----------|End
42005------- | 42049---------------|42005---------|42049
45------------ | 10--------------------|45--------------|10
42060------- | 42104---------------|42060---------|42104
45 ----------- | 10--------------------|45--------------|10
42115------- | 42159---------------|42115---------|42159
45 ----------- | 10--------------------|45--------------|10
I need a way to match the current date to the start and end dates that are listed, they need unique ID as the result of an IF function for conditional formatting. This notes that it is either the beginning date or the end date which is considered a "travel day".
Basically, I need B2 in the top example to say IF an exact match of the above date in A2 can be found in column1 or column2 of employee 1's [section] in the table, then "unique ID". What I cant figure out is how to search in BOTH employee 1 and employee 1 0.01 columns depending on the name field in the top.
The name column in the top example is a list drop down, so i have to find a dynamic formula for this. Manually assigning a range to each cells lookup fields wouldn't work.
=HLOOKUP(B$2,Sheet2!$A$1:$GR$340,MATCH($A2,Sheet2!$A$1:$GR$1),FALSE)>=100,18, "false")
Clearly that doesn't work, but was an idea to try to get the search in the right area. No way i can find the correct column for the name, then select that column +340 rows, and +1 column to the right for the proper range, then look for specific value?
Is there a way to achieve this?
If I understand this correctly, you can solve this along these lines:
Find the column matching the employee name
Search the column to see if there is a match to the date value.
If there is no match, then search the next column to the right for a matching date value.
If there is no match in the second column, then return a value to indicate failure.
This formula is going to be really long and complicated so I'll break it up as best I can. You can start by finding the column matching the employee with a match function. This will return the column number.
=match(EmployeeName, EmployeeNames, 0)
Next, we'll want to search this column with a vlookup but you have to refer to the column by column number and not by letter. To do that, use indirect(address()). Address works by using the row number and column number to refer to a cell. We'll use one for the start of the column and one for the end of the column. Plug in the match formula from above and we have the range to search.
=vlookup(DateValue,indirect(Address(1,match(EmployeeName,EmployeeNames,0))):indirect(address(500,match(EmployeeName,EmployeeNames,0))),1,0)
Note that I used 500 as the end of last row of the column to be searched. You can make it more if needed.
So far this formula will return the date if the date is found in the first column matching the employee name and an error if there is no match. We still have to search the second column if there wasn't a match. I like to use the iferror() function for this. Put the formula above as the first argument of the iferror and also as the second. We'll modify the second to search the next column over. Since we are using the column numbers, we can just increment over by adding 1 to the column.
=iferror(vlookup(DateValue,indirect(Address(1,match(EmployeeName,EmployeeNames,0))):indirect(address(500,match(EmployeeName,EmployeeNames,0))),1,0), vlookup(DateValue,indirect(Address(1,match(EmployeeName,EmployeeNames,0)+1)):indirect(address(500,match(EmployeeName,EmployeeNames,0)+1)),1,0).
Almost there, now we have to handle the error if nothing is found in the second column as well. Wrap the second vlookup in an iferror and some constant at the end.
=iferror(vlookup(DateValue,indirect(Address(1,match(EmployeeName,EmployeeNames,0))):indirect(address(500,match(EmployeeName,EmployeeNames,0))),1,0),iferror( vlookup(DateValue,indirect(Address(1,match(EmployeeName,EmployeeNames,0)+1)):indirect(address(500,match(EmployeeName,EmployeeNames,0)+1)),1,0),"No Match").
When you set up the conditional formatting, you would set the formula to check if the cell value is (or is not) "No Match".