Ideally we would like to avoid using VBA for this (if it's possible).
I have provided two sample spreadsheets to illustrate the issue as it's probably better with an example rather than giving confusing explanations.
Main sheet: https://www.dropbox.com/s/xjb5of65ru204y4/Main.xlsx?dl=0
Suppliers sheet: https://www.dropbox.com/s/hcjzwatk2m67a94/Suppliers.xlsx?dl=0
Basically, we need to pull a range of an unknown length starting from an unknown offset from a closed workbook, then use MATCH to find the row index of a value we're looking for and return the value.
We tried three different methods. All formulas works when the workbook is open but we need a solution to use without the workbook open.
I didn't look into using INDIRECT since MS strictly states that INDIRECT is not compatible with closed workbook.
Here are the formulas with indentation for better visual:
Method #1: Using index + count(if()) (array)
This solution returns #REF! when the external workbook is closed
INDEX(
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$G:$G,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
)
:
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$G:$G,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
+
COUNT(
IF('Z:\...\[Suppliers.xlsx]suppliers'!$A:$A=$L$9,1)
)-1
)
,
MATCH(
K11,
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$D:$D,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
)
:
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$D:$D,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
+
COUNT(
IF('Z:\...\[Suppliers.xlsx]suppliers'!$A:$A=$L$9,1)
)-1
)
,0)
,0)
Method #2: Using index + countif()
This solution returns #VALUE! when the external workbook is closed
INDEX(
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$G:$G,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
)
:
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$G:$G,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
+
COUNTIF('Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,$L$9))-1
)
,
MATCH(
K11,
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$D:$D,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
)
:
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$D:$D,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
+
COUNTIF('Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,$L$9))-1
)
,0)
,0)
Method #3: Using index + sumproduct()
This solution returns #REF! when the external workbook is closed
INDEX(
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$G:$G,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
)
:
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$G:$G,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
+
SUMPRODUCT( -- ('Z:\...\[Suppliers.xlsx]suppliers'!$A:$A=$L$9))-1
)
,
MATCH(
K11,
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$D:$D,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
)
:
INDEX('Z:\...\[Suppliers.xlsx]suppliers'!$D:$D,
MATCH($L$9,'Z:\...\[Suppliers.xlsx]suppliers'!$A:$A,0)
+
SUMPRODUCT( -- ('Z:\...\[Suppliers.xlsx]suppliers'!$A:$A=$L$9))-1
)
,0)
,0)
If I understand correctly, the tricky bit is building a dynamic range using INDEX(ref,row,col):INDEX(ref,row,col) since every bits works on their own but when used in this fashion it returns an error.
Note: You might need to update the absolute path to the spreadsheet suppliers.xlsx in main.xlsx if you download the provided demo
All help is gladly appreciated!
Related
I have a problem in my formula regarding on "EQUAL/MATCH" value using if and index match. I have two sheets, "Targets(database)", and "STUDENTS(input)". Example from sheet "STUDENTS" if I input 80% in Performance1, it will look to sheet Target(table) the Performance1 having equal to 80% and multiply it to the multiplier 10%. See table and sample below
TARGET(table range)
STUDENTS(correct answer)
FORMULA
=IF(
C7<Targets!$C7,
Targets!$C$5*$B7,
IFERROR(
INDEX(
Targets!$C$5:$H$5,1,
MATCH(STUDENTS!C7,Targets!$C7:$H7,0)
)*$B7,
INDEX(
Targets!$C$5:$H$5,1,
MATCH(STUDENTS!C7,Targets!$C7:$H7,1)
)*$B7
) )
Now the problem here is that when I input an "EQUAL/MATCH" value, example Performance1 80% the answer must be 3%(10%*30%) but it gives me 2.70%(9%*30%). See below incorrect answer
STUDENTS(incorrect answer)
What can I try next?
I would normally have coded my output prior if I had direct access to the system. Unfortunately, my current customer how shall I say, slightly antiquated. I have been given a csvde dump from active directory. Within Excel how I can extract all the 'CN=*,' values in cell A1 and list them into the adjacent cell A2?
A1
A2
CN=Organization Management,OU=Microsoft Exchange Security Groups,DC=test,DC=local;CN=Discovery Management,OU=Microsoft Exchange Security Groups,DC=test,DC=local;CN=VPN Users,OU=Groups,OU=TEST,DC=test,DC=local;CN=Debugger Users,CN=Users,DC=test,DC=local;CN=Wireless Users,OU=Groups,OU=TEST,DC=test,DC=local;CN=Users,DC=test,DC=local;CN=Exchange Public Folder Administrators,OU=Microsoft Exchange Security Groups,DC=test,DC=local;CN=Exchange Organization Administrators,OU=Microsoft Exchange Security Groups,DC=test,DC=local
With Excel 365 for Mac, you can do:
=LET( txt, A1,
s, SEQUENCE(LEN(txt)),
hdrSeq, (MID(txt,s,3)="CN=")*s, tlrSeq, (MID(txt,s,1)=",")*s,
header, FILTER( hdrSeq, hdrSeq > 0 ), trailer, FILTER( tlrSeq, tlrSeq > 0 ),
nxtTrlr, INDEX(trailer, IFERROR( XMATCH(header,trailer,1,-1), 1 ) ),
MID( txt, header, nxtTrlr-header) )
where A1 contains the target text that you want to parse.
If you need it to spill right, this will do it:
=LET( txt, A1,
s, SEQUENCE(LEN(txt)),
hdrSeq, (MID(txt,s,3)="CN=")*s, tlrSeq, (MID(txt,s,1)=",")*s,
header, FILTER( hdrSeq, hdrSeq > 0 ), trailer, FILTER( tlrSeq, tlrSeq > 0 ),
nxtTrlr, INDEX(trailer, IFERROR( XMATCH(header,trailer,1,-1), 1 ) ),
TRANSPOSE( MID( txt, header, nxtTrlr-header) ) )
Adding TEXTJOIN
Note JvdV's answer within the comments - super sleek and non-intuitive.
If you need this as a single cell text, then:
=LET( txt, A1,
s, SEQUENCE(LEN(txt)),
hdrSeq, (MID(txt,s,3)="CN=")*s, tlrSeq, (MID(txt,s,1)=",")*s,
header, FILTER( hdrSeq, hdrSeq > 0 ), trailer, FILTER( tlrSeq, tlrSeq > 0 ),
nxtTrlr, INDEX(trailer, IFERROR( XMATCH(header,trailer,1,-1), 1 ) ),
TEXTJOIN(",", 1, MID( txt, header, nxtTrlr-header) ) )
You may find a two-step solution useful:
use TextToColumns comand in Data tab to split string by semicolon, comma and equal signs:
use formula:
=TEXTJOIN(CHAR(10),1,"CN="&FILTER(B1:ZZ1,A1:ZY1="cn"))
How to split the data from one cell to 8 rows in excel
For example:
There is given long data in one cell: 65178492194051241284...
The sorted values should look like:
65:17:84:92
40:51:24:12
Excel offers multiple options to help you build your own formula
LEFT is used to extract characters from the LEFT of the string (also MID might be useful in your case)
https://www.excel-easy.com/examples/substring.html
Combining LEFT/MID with CONCATENATE you should be able to produce the expected output
https://edu.gcfglobal.org/en/excelformulas/using-concatenate-to-combine-names/1/
Something similar to
=CONCATENATE(LEFT(A1,2),':',MID(A1,2,2),':', MID(A1,4,2),.... )
You have Excel365 then could try-
=TEXTJOIN(":",TRUE,MID(INDEX(MID($A$1,SEQUENCE(INT(LEN($A$1)/8)+1,,1,8),8),ROW(1:1)),{1,3,5,7},2))
This will only work in Excel 365:
=MID(TEXTJOIN(":",TRUE,MID(A1,SEQUENCE(1,LEN(A1)/2,1,2),2)),SEQUENCE(LEN(A1)/8,, 0)*12+1,11)
Or if you want a more readable (and slightly faster running) version:
=LET( t,A1,
L, LEN( t ),
S, TEXTJOIN( ":", TRUE, MID( t, SEQUENCE( 1, L / 2, 1, 2 ), 2) ),
MID( S, SEQUENCE( L/8,, 0 )*12 + 1, 11 ) )
With Microsoft Excel 365 you can use:
Formula in C1:
=TEXT(MID(A1,SEQUENCE(LEN(A1)/8,,,8),8),"00\:00\:00\:00")
If you haven't always have an exact multiple of 8 characters you should nest FLOOR():
=TEXT(MID(A1,SEQUENCE(FLOOR(LEN(A1)/8,1),,,8),8),"00\:00\:00\:00")
I have a named range by name 'AlgeriaBchar'. (Algeria-> countryname and Bchar-> statename)
I use constraint on city dropdown as content in country dropdown + state dropdown (i.e. E7 + F7).
But as we can see in img staename is 'Béchar' which i need to remove char 'é' as it doesnt fit in a-z, A-Z, 0-9.
I also need to remove any blank spaces, hiphns, square brackets, apostrophy marks in country and state dropdown contents
I m not getting in which sequence I should put these fucntions to get it to 'AlgeriaBchar'
For better understanding I provided indentation to data validation fucntion in excel.
I'll be putting this fucntion in city dropdown's validaity constraint.
I may need to evaluate each character in a string which will need for loop. MID( E7, 1 ,1 ) gives me only 1st chracter. How can I loop it till the length of the string.
can anyone suggest me.
=IF(
NOT(
AND( CODE( MID( E7, 1 ,1 ) ) >=65 , CODE( MID( E7, 1 ,1 ) ) <=90 )
)
AND(
NOT(
AND( CODE( MID( E7, 1 ,1 ) ) >=97 , CODE( MID( E7, 1 ,1 ) ) <=122 )
)
)
AND(
NOT(
AND( CODE( MID( E7, 1 ,1 ) ) >=48 , CODE( MID( E7, 1 ,1 ) ) <=57 )
)
)
, SUBSTITUTE( E7, MID( E7, 1 ,1 ), "" ) ,""
)
INDIRECT(
SUBSTITUTE(
SUBSTITUTE(
SUBSTITUTE(
SUBSTITUTE(
SUBSTITUTE(UPPER($E$7)," ","")
,"-","")
,"'","")
,"[","")
,"]","")
)
)
You may want to use a macro for that. Consider this one (based on this article from extendoffice.com)
1) Hold down the Alt + F11 keys in Excel, and it opens the Microsoft Visual Basic for Applications window.
2) Click Insert > Module, and paste the following macro in the Module Window.
Function StripAccent(thestring As String)
Dim A As String * 1
Dim B As String * 1
Dim i As Integer
Const AccChars = "ŠŽšžŸÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðñòóôõöùúûüýÿ-[]' "
For i = 1 To Len(AccChars)
A = Mid(AccChars, i, 1)
thestring = Replace(thestring, A, "")
Next
StripAccent = thestring
End Function
Function TrimmedConcat(A As String, B As String)
TrimmedConcat = StripAccent(A) & StripAccent(B)
End Function
Then in your document, click in a cell, and use TrimmedConcat as if it was a normal function, selecting the country name as the first argument and the city name as the second.
In your example =TrimmedConcat(E7,F7)
I hope that helps!
In Excel 2010, I have set up a two dimensional table that is filled with percent errors. These percentages are calculated based on a user input, so the values change depending on what value the user asks for.
What I would like to do is be able to find the location of the cell in the table that is closest to zero, and return the row and column of that data point for further calculations. Is there a way to do this without using a macro?
Thanks in advance for any help.
EDIT: The percent errors are non-negative, and may appear more than once in the table.
That would be too unreliable for cells with the same absolute value closest to 0 (for example -1 and 1).
This array formula will get the absolute value closest to 0:
{ = MIN( ABS( Table1 ) ) }
Where Table1 is the name of the table or range. If the table has values -1 and 1, then it will return 1.
Then those array formulas to get the max row and max column of the values closest to 0
{ = MAX( ROW(Table1) * (ABS(Table1) = MIN(ABS(Table1))) ) }
{ = MAX( COLUMN(Table1) * (ABS(Table1) = MIN(ABS(Table1))) ) }
Update
This array formula should work for your mirrored data:
{ = ADDRESS( MIN( IF( ABS(Table1) = MIN(ABS(Table1)), ROW(Table1) ) ),
MAX( IF( ABS(Table1) = MIN(ABS(Table1)), COLUMN(Table1) ) ) ) }
You can remove the ABS parts if there are no negative values:
{ = ADDRESS( MIN( IF(Table1 = MIN(Table1), ROW(Table1) ) ),
MAX( IF(Table1 = MIN(Table1), COLUMN(Table1) ) ) ) }
The trick is that Min and Max ignore logical values, so I omitted the third parameter of the If
Update 2
This array formula is a bit simplified version of #JohnBustos 's answer (I am still not sure why he uses SMALL instead of MIN):
{ = MIN( IF(Table1=MIN(Table1), ROW(Table1)*1000 +COLUMN(Table1)) ) }
This will return number like 3005 where 3 is the row and 5 is the column, so the address is:
{ = ADDRESS( MIN( IF(Table1=MIN(Table1), ROW(Table1)) ),
MOD(MIN( IF(Table1=MIN(Table1), ROW(Table1)*10^5 +COLUMN(Table1)) ), 10^5) ) }
The VBA version will be much simpler, because of the Range.Find method:
Function findMin(r As Range) As Range
Set findMin = r.Find(WorksheetFunction.Min(r))
End Function
with sample uses:
=CELL("address", findMin(Table1))
=ROW(findMin(Table1))
=COLUMN(findMin(Table1))
I'm curious to see how others do this, but, for the sake of example, suppose your data was set up in the range A1:G7. You could do this via the following array function (meaning press CTRL+SHIFT+ENTER after entering the formula) as follows:
=ADDRESS(MIN(IF(ABS(A1:G7)=SMALL(ABS(A1:G7),1),ROW(A1:G7)),MIN(IF(ABS(A1:G7)=SMALL(ABS(A1:G7),1),COLUMN(A1:G7))))
Note that if you wanted the row and column values separately, they are in this formula too:
Row: = MIN(IF(ABS(A1:G7)=SMALL(ABS(A1:G7),1),ROW(A1:G7)))
Col: = MIN(IF(ABS(A1:G7)=SMALL(ABS(A1:G7),1),COLUMN(A1:G7)))
Hope this helps.
UPDATE:
Given the comment made (which I completely missed) that if the value appeared more than once you could get a different result for the row and column values, I made up a new formula that will give you the result you want.
Give this a try:
=CELL("address",INDIRECT(TEXT(SMALL(IF(ABS(B2:GS201)=SMALL(ABS(B2:GS201),1),ROW(B2:GS201)*10^4+COLUMN(B2:GS201)),1), "R0000C0000"),0))