Related
Currently I am using the following formula in successive rows on a sheet named Data
{=IFERROR(SMALL(IF($Q$2=DMS!$I$1:$I$99999,ROW(DMS!$I$1:$I$99999)-ROW(DMS!$I$1)+1),1),"")}
{=IFERROR(SMALL(IF($Q$2=DMS!$I$1:$I$99999,ROW(DMS!$I$1:$I$99999)-ROW(DMS!$I$1)+1),2),"")}
{=IFERROR(SMALL(IF($Q$2=DMS!$I$1:$I$99999,ROW(DMS!$I$1:$I$99999)-ROW(DMS!$I$1)+1),3),"")}
...
For a total of 30 rows to determine the first 30 rows within Sheet DMS column I that match the value in my sheet 'Data' cell Q2.
I am trying to find a way to either recreate the above arrays, or an alternate solution to using the arrays, when the searched range is no longer going to be a fixed location. Essentially what was in column I could be in column J tomorrow and H the next day and back to I for the next 3 days. I will be able to tell where it is by the column header, so I do have one static point of reference, even if it is not in a fixed column.
Try this formula. It looks for a column with the text "TheKey" in the first row of columns A to Z and will then perform the match on the values in the found column.
In Excel 365, you can just hit enter, in earlier versions, you need to use Ctrl+Shift+Enter
=IFERROR(SMALL(IF($Q$2=INDEX(dms!$A$1:$Z$9999,1,MATCH("TheKey",dms!$A$1:$Z$1,0)):INDEX(dms!$A$1:$Z$9999,9999,MATCH("TheKey",dms!$A$1:$Z$1,0)),ROW($A$1:$A$9999)),ROW(A1)),"")
In Excel 365, you can avoid the duplication of the Match function by applying the new Let() function and re-using the result. Like this:
=LET(myColumn,MATCH("TheKey",dms!$A$1:$Z$1,0),IFERROR(SMALL(IF($Q$2=INDEX(dms!$A$1:$Z$9999,1,myColumn):INDEX(dms!$A$1:$Z$9999,9999,myColumn),ROW($A$1:$A$9999)),ROW(A1)),""))
I removed the -ROW(DMS!$I$1)+1 because that does not do anything, really. Just subtracts 1 and then adds 1 again.
I also replaced the hard-coded number for subsequent rows (1, 2, 3, etc) with Row(A1), which will evaluate to 1 in the first row of the formula and when copied down will turn into 2, 3, 4, etc without having to adjust the formula for each row.
You need to combine several advanced functions. First of all, you need to locate that header, because it's your only static point.
You can locate your static header using:
=CELL("address";INDEX(DMS!$A$1:$M$1;;MATCH("STATIC HEADER";DMS!$A$1:$M$1;0)))
In this case, it will return this text: [Libro1]DMS!$I$1
It's the complete path to cell.
Now you need to combine that string, with OFFSET to get a reference to that column and get like 99999 rows of data. ISsue here is OFFSET does not work on different worksheets from active one, so you need to combine it with INDIRECT first to get a reference to range in sheet DMS:
INDIRECT function
OFFSET function
So then a formula like next one will return a reference of 1 column and 99999 cells where your static header is:
OFFSET(INDIRECT(CELL("address";INDEX(DMS!$A$1:$M$1;;MATCH("STATIC HEADER";DMS!$A$1:$M$1;0))));0;0;99999;1)
So now you can combine all of these into a really huge formula:
=IFERROR(SMALL(IF($Q$2=OFFSET(INDIRECT(CELL("address";INDEX(DMS!$A$1:$M$1;;MATCH("STATIC HEADER";DMS!$A$1:$M$1;0))));0;0;99999;1);ROW(OFFSET(INDIRECT(CELL("address";INDEX(DMS!$A$1:$M$1;;MATCH("STATIC HEADER";DMS!$A$1:$M$1;0))));0;0;99999;1)));ROW()-5);"")
Notice I've deleted the ROW(DMS!$I$1)+1 because that's always equal to 0, so not needed. I addedd in SMALL function the part ROW()-5) so I can drag down (i'm testing in row 6 and dragging down. No need of those 1, 2, 3 you input manually). Adapt this part to your needs.
Notice that if STATIC HEADER is in a different column, the formula still works.
I've uploaded a file do my Gdrive in case you want to see how it works, because copying so many functions can be crazy.
https://drive.google.com/file/d/16Y6mQ_hiu1TKLXhTUgvq8tN67eHQKtVb/view?usp=sharing
Here I am stucked with one excel issue where i want to concatenate from column F till column I where the logic is when the benchmark column A3 (for example) is blank it need to concatenate column F till column I till there is a value at column A4.and this logic need to automatically concatenate the mentioned column till there is a value under the benchmark column. currently i need to keep change the concatenate range in order to concatenate it fully with the logic. Appreciate if anyone can help me out.
Below image shows how i am doing manually which very time consuming
You can use the MATCH function (with a wildcard) to find the next non-blank row; and use that in an INDEX function to detect the range to concatenate.
Assuming your data starts in A3 and the lowest possible row is row 1000 (change the 1000's in the formula below if it might be much different:
J2: =IF(A2="","",CONCAT(INDEX(F2:$I$1000,1,0):INDEX(F2:$I$1000,IFERROR(MATCH("*",A3:$A$1000,0),1000-ROW()),0)))
Note: It is possible to also develop solutions using INDIRECT and/or OFFSET. Unfortunately, these functions are volatile, which means they recalculate anytime anything changes on your worksheet. If there are a number of formulas using these functions, worksheet performance will be impaired. INDEX and MATCH are non-volatile (except in ancient versions of Excel - pre-2003 or so)
The OFFSET-function would come on handy here. One solution is to do it like
This works in my worksheet.
Cell Q6 just defines the number of rows downwards that the MATCH-function is checking for the next "HEADER1" value. If "HEADER1" is found, the MATCH-function returns how many rows down-1. If no "HEADER1"-value is found within that range, that value is then the number of rows used.
If the first column also has "HEADER2" and so on, you can add the MID-function to both references inside MATCH to limit which part of the string are to be searched for.
I tried to adjust the references properly to fit your sheet, but I may have missed something:
=IF(ISBLANK($B2),"",CONCAT(OFFSET($B2,0,0,IFNA(MATCH(MID($B2,1,6),MID(OFFSET($B2,1,0,$B$1),1,6),0),$B$1),4)))
I want to average every 5 rows but also to exclude in the average values that are less than 50. This is the command to average every 5 rows.
=AVERAGE(OFFSET($L$3,(ROW()-ROW($P$2))*5,,5))
This is the command to exclude values less than 50
=AVERAGEIF(L3:L8,">50")
How do I combine those two in one command?
Thanks to a colleague of mine, the following works like a gem.
=IFERROR(AVERAGEIF(OFFSET($L$3,(ROW()-ROW($P$2))*5,,5),">50"),0)
As long as you have Excel 2016, a SUMPRODUCT formula will work.
=SUMPRODUCT((MOD(ROW($A$1:$A100),5)=0)*($A$1:$A$100<50)*$A$1:$A$100)/SUMPRODUCT((MOD(ROW($A$1:$A100),5)=0)*($A$1:$A$100<50)*1)
I assumed your data was in A1:A100 so update that as needed. And in the MOD formula, I used 5 for ever 5th row. If you need to change that, change the 5 in MOD formulas. Lastly, the formula does not include the value 50 since you stated you wanted less than 50.
'Tiny' Differences
Correction
=IFERROR(AVERAGEIF(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5),">50"),0)
Visualize
Let's first visualize what you're actually doing.
For every five rows in column L you are displaying the average of the values, if they are greater than or equal to 50. (in this example) in column G:
For L3:L8 in G2,
for L9:L13 in G3,
for L14:L18 in G4 etc.
Issues
The 1st issue is that the formula is written exclusively for the
2nd row. If you want the first result to be displayed in the first row, the formula will result in a REF! error.
If you want to display the first result in the 1st row you have to
change L$2 to L$1:
=IFERROR(AVERAGEIF(OFFSET(L$3,(ROW()-ROW(L$1))*5,,5),">50"),0)
or for the 3rd row you have to change L$2 to L$3:
=IFERROR(AVERAGEIF(OFFSET(L$3,(ROW()-ROW(L$3))*5,,5),">50"),0)
The 2nd issue is that you are doing something in column L and for
no obvious reason you are using column P in your formula. You could
have used any column Z, AN or CG, but your doing stuff in
column L, so use L.
The 3rd issue is that you have locked the columns $L which means where
ever you put the formula in a single row, the result will be the same. If
you don't lock them, you can copy the formula e.g. to the right and
it will display the results for columns M, N, O etc.:
Other Formulas
=SUM(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5))
=COUNT(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5))
=AVERAGE(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5))
=SUMIF(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5),">50")
=COUNTIF(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5),">50")
AVERAGEIF is available in Excel from version 2007, but for older versions the following formula can be used instead:
=IF(COUNTIF(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5),">"&50)=0,0,SUMIF(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5),">"&50)/COUNTIF(OFFSET(L$3,(ROW()-ROW(L$2))*5,,5),">"&50))
It first checks if COUNTIF results in 0. If it does it displays 0, otherwise it divides SUMIF with COUNTIF.
EDIT: I have revived the source data source to remove the ambiguity of my last screen shots
I am trying to transpose spreadsheet data where there are many rows where the customer name may be duplicated but each row contains a different product.
For instance
revised original data source
to
revised proposed data format
I would like to do it with formulae if possible as I struggle with VB
Thank you for any help
I realise this is a huge answer, apologies but I wanted to be clear. If you need anything from me, drop me a comment and I'll help out.
Here's the output from my formula:
EDITED ANSWER - Named ranges used for ease of understanding:
These are just an example of a few of the named ranges I have used, you can reference the ranges directly or name them yourself (simplest way is to highlight the data then put the name in the drop down next to the formula bar [top left])
Be wary that as we will be using Array formulas for AccNum and AccType, you will not want to select the entire column and instead opt for either the exact data length or overshoot it by 100 or so. Large array formulas tend to slow down calculation and will calculate every cell individually regardless of it being empty.
First formula
=IF(COUNTIF(D2:D11,">""")>0,CONCATENATE("Account Number ",LEFT((COLUMN(A:A)+1)/2,1)),"")
This formula is identical to the one in the original answer apart form the adjusted heading title.
=IF(Condition,True,False) - There are so many uses for the IF logic, it is the best formula in Excel in my opinion. I have used to IF with COUNTIF to check whether there is more than 0 cells that are more than BLANK (or ""). This is just a trick around using ISBLANK() or other blank identifiers that get confused when formula is present.
If the result is TRUE, I use CONCATENATE(Text1,Text2,etc.) to build a text string for the column header. ROW(1:1) or COLUMN(A:A) is commonly used to initiate an automatically increasing integer for formulas to use based on whether the count increase is required horizontally or vertically. I add 1 to this increasing integer and divide it by 2 so that the increase for each column is 0.5 (1 > 1.5 > 2 > 2.5) I then use LEFT formula to just take the first digit to the left of this decimal answer so the number increases only once every 2 columns.
If the result is FALSE then leave the cell blank ,""). Standard stuff here, no explanation needed.
Second Formula
=CONCATENATE(INDEX(Forename,MATCH(Sheet4!$A2,Reference,0)))
=CONCATENATE(INDEX(Surname,MATCH(Sheet4!$A2,Reference,0)))
CONCATENATE has only been used here to force blank cells to remain blank when pulled by INDEX. INDEX will read blank cells as values and therefore 0's whereas CONCATENATE will read them as text and therefore "".
INDEX(Range,Row,Column): This is a lookup formula that is much more advanced than VLOOKUP or HLOOKUP and not limited in the way that they are.
The range i have used is the expected output range - Forename or Surname
The row is then calculated using MATCH(Criteria,Range,Match Type). Match will look through a range and return the position as an integer where a match occurs. For this I have set the criteria to the unique reference number in column A for that row, the range to the named range Reference and the match type as 0 (1 Less than, 0 Exact Match, -1 Greater than).
I did not define a column number for INDEX as it defaults to the first column and I am only giving it one column of data to output from anyway.
Third Formula
Remember these need to be entered as an array (when in the formula bar hit Ctrl+Shift+Enter)
=IFERROR(INDEX(AccNum,SMALL(IF(Reference=Sheet4!$A2,ROW(Reference)-ROW(INDEX(Reference,1,1))+1),ROUNDDOWN((COLUMN(A:A)+1)/2,0))),"")
=IFERROR(INDEX(AccType,SMALL(IF(Reference=Sheet4!$A2,ROW(Reference)-ROW(INDEX(Reference,1,1))+1),ROUNDDOWN((COLUMN(B:B)+1)/2,0))),"")
As you can see, one of these is used for AccNum and the other for AccType.
IFERROR(Value): The reason that this has been used is that we are not expecting the formula to always return something. When the formula cannot return something or SMALL has run out of matches to go through then an error will occur (usually #VALUE or #NUM!) so i use ,"") to force a blank result instead (again standard stuff).
I have already explained the INDEX formula above so let's just dive in to how I have worked out the rows that match what we are looking for:
SMALL(IF(Reference=Sheet4!$A2,ROW(Reference)-ROW(INDEX(Reference,1,1))+1),ROUNDDOWN((COLUMN(B:B)+1)/2,0))
The IF statement here is fairly self explanatory but as we have used it as an array formula, it will perform =Sheet4!$A2 which is the unique reference on every cell in the named range Reference individually. In your mock data this returns a result of: {FALSE;TRUE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE} for the first entry (I included titles in the range, hence the initial FALSE). IF will do my row calculation* for every true but leave the FALSEs as they are.
This leaves a result of {FALSE;2;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE} that SMALL(array,k) will use. SMALL will only work on numeric values and will display the 'k'th result. Again the column trick has been used but to cover more ground, I used another method: ROUNDDOWN(Number,digits) as opposed to using LEFT() Digits here means decimal places so I used 0 to round down to a whole integer for the same result. As this copies across the columns like so: 1, 1, 2, 2, 3, 3, SMALL will alternatively (as the formulas alternate) grab the 1st smallest AccNum then the 1st Smallest AccType before grabbing the 2nd AccNum and Acctype and so forth.
*(Row number of the match minus the first row number of the range then plus 1, again fairly common as a foolproof way to always get the correct row regardless of where the data starts; actually as your data starts on row 1 we could just do ROW(Reference) but I left it as is incase you had data in a different format)
ORIGINAL ANSWER - Same logic as above
Here's your solution in 3 parts
Part 1 being a trick for the auto completion of the titles so that they will hide when not used (in case you will just copay and paste values the whole lot to speed up use again).
=IF(COUNTIF(C2:C11,">""")>0,CONCATENATE("Product ",LEFT((COLUMN(A:A)+1)/2,1)),"") in C
=IF(COUNTIF(D2:D11,">""")>0,CONCATENATE("Prod code ",LEFT((COLUMN(B:B)+1)/2,1)),"") in D
Highlight both of the cells and drag across to stagger the outputs "Product " and "Prod code "
Part 2 would be inputting the unique IDs to the new sheet, I would suggest copying your entire column A across to a new sheet and using DATA > REMOVE DUPLICATES > Continue with current selection to trim out the multiple occurrences of unique IDs.
In column B use =INDEX(Sheet2!$B$1:$B$7,MATCH(Sheet4!$A2,Sheet2!$A$1:$A$7,0)) to get the names pulled across.
Part 3, the INDEX
Once again, we are doing a staggered input here before copying the formula across the page to cover the entirety of the data.
=IFERROR(INDEX(Sheet2!$C$1:$D$11,SMALL(IF(Sheet2!$A$1:$A$11=Sheet4!$A2,ROW(Sheet2!$A$1:$A$11)-ROW(INDEX(Sheet2!$A$1:$A$11,1,1))+1),ROUNDDOWN((COLUMN(A:A)+1)/2,0)),1),"") in C
=IFERROR(INDEX(Sheet2!$C$1:$D$11,SMALL(IF(Sheet2!$A$1:$A$11=Sheet4!$A2,ROW(Sheet2!$A$1:$A$11)-ROW(INDEX(Sheet2!$A$1:$A$11,1,1))+1),ROUNDDOWN((COLUMN(B:B)+1)/2,0)),2),"") in D
The formulas of Part 3 will need to be entered as an array (when in the formula bar hit Ctrl+Shift+Enter) . This will need to be done before copying the formulas across.
These formulas can now be dragged / copied in all directions and will feed off of the unique ID in column A.
My Answer is already rather long so I haven't gone on to break the formula down. If you have any trouble understanding how this works, let me know and I will be happy to write up a quick guide, breaking it down chunk by chunk for you.
I have 12 sheets in one Google Sheets document labeled for each month (January - December). On each sheet column A contains a project number, e.g. "6091".
I'm trying to find a function that will check all of the other sheets to see if there are duplicate cells in the "project number" column of other sheets.
So: "Do any of the cells in column A, match any of the cells in column A on other sheets".
Is there a quick way to do this?
The formula =arrayformula(iferror(match(A2:A, AnotherSheet!A2:A, 0))) checks each value in A2:A of the present sheet for being in A2:A of AnotherSheet. If it's there, it returns the position in AnotherSheet, otherwise the output is empty (the error #N/A is suppressed by iferror).
You can use the above for each of the sheets separately. Alternatively, if you are not interested in the positions and just want to know which entries from A2:A are found elsewhere, then add the results for each sheet:
=arrayformula(iferror(match(A2:A, AnotherSheet!A2:A, 0)) + iferror(match(A2:A, ThirdSheet!A2:A, 0)))
The output is 0 is there is no match, and a nonzero number if there is.
You may try using this to find the number of duplicates:
=counta('JAN'!A:A,'FEB'!A:A)-countA(unique({'JAN'!A:A;'FEB'!A:A})
Where A:A is your column for the data you want to check and the respective files.
This formula counts the total number of data you have, minus the unique data, therefore giving you the total number of duplicates in your dataset.
This formula gives you an overview of the total number of duplicates, however, it doesn't show which cell is a duplicate.
Alternatively, you can add a checker column and input the following formula to check if that specific cell is a duplicate.
=match(cell to check,{range 1;range 2;...range 12})
Alternatively, you may use this formula to find the exact duplicates between the ranges, however this formula does not search within the range itself for duplicates.
=arrayformula(filter(range to check,(countif(arrayformula({Range 1;range 2}),{range to check}))>1))
Personally I think that the last option would be the best as it gives you the exact data to correct.
I am still new to this so hope this helps:)
This formula should work for numerical values:
=COUNT(QUERY({March!A:A;April!A:A},"where Col1="&A2))
If you are searching for text values it would be
=COUNTA(QUERY({March!A:A;April!A:A},"where Col1='"&A2&"'"))
Unfortunately the QUERY function does not work within an arrayformula so you would need to copy the formula down the column. You can add in extra sheets into the { } array as required, separated by a semi-colon
Edit: actually, borrowing from #sandwich, this version should work without the need to copy the formula down the column:
=arrayformula(iferror(match(A2:A,{March!A:A;April!A:A},0)))