VBA : distinct values in a column and their associated distinct values - excel

I'm trying to do something similar in vba which I have an idea of only in python for loops. Can someone teach me how to do this in vba, either in function or module macro please :
For each distinct values in column A4:A30, there should be no more than 9 distinct values in column C4:C30. If true, return 'OK' in cell A1. if false, return 'Error' in cell A1'
e.g As in the picture, Sam should not have more than 9 distinct fruits. Same goes to Mary
Update :
I have tried the filterxml method and unfortunately didn't seem work for me : [1] https://i.stack.imgur.com/cbmTs.png

Solution for excel with filter/unique formulas
Easiest way to achieve it in Excel365 is: add extra column which counts unique values (Fruits) for each Key (Names) and find maximum value in this column
Start with formula that find each non-blank which fits the key.
=FILTER($C$4:$C$30,($A$4:$A$30=A4)*($C$4:$C$30<>""))
Then delete duplicates:
=UNIQUE(FILTER($C$4:$C$30,($A$4:$A$30=A4)*($C$4:$C$30<>"")))
Then check how many cells we have in filtered data without blanks and duplicates:
=COUNTA(UNIQUE(FILTER($C$4:$C$30,($A$4:$A$30=A4)*($C$4:$C$30<>""))))
Then expand our new-column (column B in my case) formula to each row in our Keys.
And finally add formula to A1 which checks maximum counter:
=IF(MAX($B$4:$B$30)<10,"OK","Error - to many velues")
*There is a little typo, it should be "Error - to many values" =)
Below how the worksheet looks in my testfile
Solution for older versions of excel
I've checked if i am able to make it works without these formulas and it is possible:
We need to start with counting if there is for key-value above current row
=COUNTIFS($A$4:A4,A4,$C$4:C4,C4)
In case we have duplicates above, they should be already counted so we skip them:
=IF(COUNTIFS($A$4:A4,A4,$C$4:C4,C4)>1,"",1)
Now we have colum with "1" or blanks. In that case we need to count each non-empty cell above which correct key (name) and add 1 so instead "" and "1" we will have "" or 1, 2, 3, 4, ...
=IF(COUNTIFS($A$4:A4,A4,$C$4:C4,C4)>1,"",COUNTIFS($A$3:A3,A4,$F$3:F3,">0")+1)
Edit
I have added one extra IF to skip keys if value is blank:
=IF(C4="","",IF(COUNTIFS($A$4:A4,A4,$C$4:C4,C4)>1,"",COUNTIFS($A$3:A3,A4,$B$3:B3,">0")+1))
Cells Formula in A1 is the same
=IF(MAX($B$4:$B$30)<10,"OK","Error - to many values")
Quick Note:
Some formulas have range which starts on 3rd row instead of 4th; Its intended because we are counting cells above and at first row of data we need to have choose something above. This code assumes that you don't have numbers (on column B) or names (on column A) in row 3;
Below I am attaching screen with example; This screen have additional columns (D-F) which isn't required, its only do display how final formula was created.

Related

Alternative to Vlookup that does not stop at first value found

I am trying to populate B2:D5 with "yes"/"No" ( Figure 1) based on the criteria that if I find the respective pvalue( Column A) , in the 'Test' column in a separate sheet and the Fvalue matches the column header of figure 1. I tried using the formula visible in figure 3. However, it incorrectly labels the cow and chicken columns. Which I suspect is due to it stopping at the first " True" value it finds, and not iterating over the other values once it finds said true value.
Use COUNTIFS()
In B2:
=IF(COUNTIFS(Sheet1!A:A,$A2,Shee1!B:B,B$1),"Yes","No")
Then copy over and down the grid.
Where Sheet1 is the list.

Multiple Conditional If Statements in Excel to Return Single Value [duplicate]

I am trying to populate B2:D5 with "yes"/"No" ( Figure 1) based on the criteria that if I find the respective pvalue( Column A) , in the 'Test' column in a separate sheet and the Fvalue matches the column header of figure 1. I tried using the formula visible in figure 3. However, it incorrectly labels the cow and chicken columns. Which I suspect is due to it stopping at the first " True" value it finds, and not iterating over the other values once it finds said true value.
Use COUNTIFS()
In B2:
=IF(COUNTIFS(Sheet1!A:A,$A2,Shee1!B:B,B$1),"Yes","No")
Then copy over and down the grid.
Where Sheet1 is the list.

Excel Check for multiple value in one row within a range

I have a spreadsheet with thousands of rows and numerous columns. What I am hoping to accomplish is to basically check column AB for a certain value. When it finds a row that contains (not exact match, just needs to include) the word "test" then I want to check if column Z (in the same row) has value "next". If both those are true, then I want to display the value from column N in that same row. What code could I use to accomplish this?
Note: I need this to list the value from column N for each row that has both "test" and "next", not just for one row.
Edit:
To clarify what I mean by multiple values see this image:
I want a formula that will list ALL of the Column N values of rows that meet have both test and next. And this list is not in the row, it is going to be one column listing each match.
Update:
I have gotten close to getting this done, this is my best formula up to this point. =ArrayFormula(IF(ISNUMBER(MATCH("*"&$A$1&"*",SID!AB:AB,0)),SID!N3,"n")) This works once, but here is what I need fixed:
This returns the first value that works, in this example Place1, but then it just lists everything form column C, even if it does not reach the requirements. Most likely an issue with my arrayformula.
Try out the below formula,
=IF(AND(ISNUMBER(MATCH("*"&"test"&"*",A1,0)),ISNUMBER(MATCH("*"&"next"&"*",B1,0))),"Column N value","")
This formula searches cell A1 for the string test (anywhere in it) and searches next in cell B1. I leave the part of replacing the cells with the required columns to you and also change the Column N value in the formula to N1

Nicer solution for how to combine cells between two values in a second column

Even though I came up with a solution for my problem, I was hoping someone might help find me a new one that is not so "cumbersome"
Here is my situation:
a cell in column A either contains a value (time stamp) or is blank
the number of blank cells between each time stamp varies
a cell in column B always contains a value (string)
Here is what I need:
in column C, combine the strings in column B between time stamps in column A
Since I know that the maximum number of blanks in Column A between two time stamps is 5, I created this function..(I deleted the space between "" and inserted a line break in front of every IF for display reasons)
=IF(AND(ISNUMBER(A1),A2="",A3="",A4="",A5=""),B1&""&B2&""&B3&""&B4&""&B5,
IF(AND(ISNUMBER(A1),A2="",A3="",A4=""),B1&""&B2&""&B3&""&B4,
IF(AND(ISNUMBER(A1),A2="",A3=""),B1&""&B2&""&B3,
IF(AND(ISNUMBER(A1),A2=""),B1&""&B2,
IF(ISNUMBER(A1),B1,"")))))
If the maximum number of blanks would be 10, the formula would get extremely long...so any ideas how to come to the same solutions in a more elegant way are appreciated!
Thanks,
Ivana
If you're able to add some columns, you could simplify your formula.
I would add three new columns instead of just one. My solution assumes that you have a header at the top in row 1. It also allows for more than five blanks between timestamps, if that ever arises.
In row 2, enter the following formulas:
Column C: =IF(ISNUMBER(A2),B2,C1 & " " & B2)
Column D: =IF(ISNUMBER(A2),A2,D1)
Column E: =IF(D2<>D3,C2,"")
Paste or drag these down to the end of your timestamps and strings to fill in the columns.
Columns D and E will contain your timestamp and concatenated B string(s). You can either pick them off or filter them to get the non-blank strings.
EDIT FOR DUPLICATE TIMESTAMPS
If duplicates can occur and you need to track them separately, then change column E in row 2 as follows:
Column E: =IF(ISNUMBER(A3),C2,IF(ISBLANK(B3),C2,""))
Copy down as before.
You can apply some old INDEX function range addressing together with the newer TEXTJOIN function¹.
  
In C2 as,
=IF(A2<>TEXT(,), IFERROR(TEXTJOIN(CHAR(44), TRUE, B2:INDEX(B:B, MATCH(1E+99, A$1:A1))), TEXT(,)), TEXT(,))
Fill down as necessary.
¹ TEXTJOIN function became available with Excel 2016 with Office 365 and Excel Online.

Is there a 2 Value Look up function in MS Excel that can perform the following?

I am going crazy over this. It seems so simple yet I can't figure this out. I have two worksheets. First worksheet is my data. Second is like an answer key. Upon checking checking, A1:B1 in Sheet 1 is a match with the conditions in Row 52 in SHEET 2, therefore, the value in Column C is "MGC". What is the formula that will perform this function? It's really hard to explain without the data so I pasted a link of the sample spreadsheet. Thank you so much in advance.
sample spreadsheet here. https://docs.google.com/spreadsheets/d/1_AjuNfCdGfEM-XkqPa6W4hSIxQg4NM2Vg4c2C1pQ_vQ/edit?usp=sharing
screenshot here. (wont let me post i have no reputation)
In Sheet2, insert a column in front of Column A and put the formula in A2 =C2&D2.
Then in Sheet1, Cell C2 the formula =vlookup(A2&B2,Sheet2!A:B,2,0).
the first make a concatenated key to lookup, then the second looks up that key.
How about a index(match())? If I've understood correctly you need to match across both the A and B column in sheet one, checking for the relevant values in B and C on sheet 2 to retrun worksheet 2 column a to worksheet 1 column c.
third version try:
=INDEX(Sheet2!$C$1:$C$360,MATCH(Sheet1!A1&Sheet1!B1,Sheet2!$B$1:$B$360&Sheet2!$C$1:$C$360,0))
Basically what this does is use concatenation, the & operator, to specify you are looking for "Criteria A" & "Criteria B" in sheet 1, which makes the string "Criteria A Criteria B", which is supplied in the first part of the match function.
In the second it then says match this against all of my variables in sheet 2 in the same way with concantenation.
The final part of match function (0) specifies you want an 'exact' match
It then supplied this as a reference to the index function, which then finds the row intersecting with the value you want, and returns that.
As noted here https://support.microsoft.com/en-us/kb/59482 this is an array formula, so it behaves differently, and must be input differently. https://support.office.com/en-za/article/Guidelines-and-examples-of-array-formulas-7d94a64e-3ff3-4686-9372-ecfd5caa57c7
There are (at least) 2 ways you could do this without VBA.
USING A SORTED LIST
The first relies on the assumption that your data can be re-sorted, so that everything "Unreported" is in the top, and everything "reported" is together below that (or vice versa). Assuming that this is the case (and it appears to already be sorted like this),we will use the function OFFSET to create a new range which shows only the values that align with either being "Unreported" or "Reported".
Offset takes a given reference to a point on a sheet, and then moves down/up & left/right to see what reference you want to return. Then, it returns a range of cells of a given height, and a given width. Here, we will want to start on Sheet2 at the top left, moving down until we find the term "Unreported" or "Reported". Once that term is found, we will want to move one column to the right (to pull column B from sheet 2), and then have a 'height' of as many rows as there are "unreported" or "reported" cells. This will look as follows in A1 on sheet 1, copied down:
=OFFSET(Sheet2!$A$1,MATCH(A1,Sheet2!A:A,0)-1,1,COUNTIF(Sheet2!A:A,A1),1)
This says: First, start at cell A1 on sheet2. Then find the term in A1 (either "unreported" or "reported", on sheet2!A:A (we subtract 1 because OFFSET starts at A1 - so if your data starts at A1 we need to actually stay at "0". If you have headers on sheet2, you will not need this -1). Then, move 1 column to the right. Go down the rows for as many times as Sheet2 column A has the term found in Sheet1 A1. Stay 1 column wide. Together, this will leave you with a single range on sheet2, showing column B for the entire length that column A matches your term in sheet1 A1.
Now we need to take that OFFSET, and use it to find out when the term in Sheet1 B1 is matched in Sheet2 column B. This will work as follows:
=MATCH(B1,[FORMULA ABOVE],0)
This shows the number of rows down, starting at the special OFFSET array created above, that the term from B1 is matched in column B from sheet2. To use this information to pull the result from column C on sheet 2, we can use the INDEX function, like so:
=INDEX([FORMULA ABOVE],MATCH(B1,[FORMULA ABOVE],0))
Because this would be fairly convoluted to have in a single cell, we can simplify this by using VLOOKUP, which will only require the OFFSET function to be entered a single time. This will work as follows:
=VLOOKUP(B1,[FORMULA ABOVE],2,0)
This takes the OFFSET formula above, finds the matching term in B1, and moves to the 2nd column to get the value from column C in sheet2. Because we are going to use VLOOKUP, the offset formula above will need to be adjusted to provide 2 columns of data instead of 1. Together, this will look as follows:
FINAL FORMULA FOR SHEET1, C1 & COPIED DOWN
=VLOOKUP(B1,OFFSET(Sheet2!$A$1,MATCH(A1,Sheet2!A:A,0)-1,1,COUNTIF(Sheet2!A:A,A1),2),2,0)
OPTION USING ARRAY FORMULAS
The above method will only work if your data is sorted so that the REPORTED and UNREPORTED rows are grouped together. If they cannot be sorted, you can use an ARRAY FORMULA, which essentially takes a formula which would normal apply to a single cell, and runs it over an entire range of cells. It returns an array of results, which must be reduced down to a single value. A basic array formula looks like this [assume for this example that A1 = 1, A2 = 2...A5 = 5]:
=IF(A1:A5>3,A1:A5,"")
Confirm this (and all array functions) by pressing CTRL + SHIFT + ENTER, instead of just ENTER. This looks at each cell from A1:A5, and if the value is bigger than 3, it gives the number from that cell - otherwise, it returns "". In this case, the result would be the array {"";"";"";4;5}. To get the single total of 9, wrap that in a SUM function:
=SUM(IF(A1:A5>3,A1:A5,""))
In your case, we will want to use an array formula to see what row in Sheet2 matches A1 from Sheet1, and B1 from Sheet1. This will look like this:
=IF(Sheet2!$A$1:A$100=A1,IF(Sheet2!$B$1:$B$100,ROW($B$1:$B$100),""),"")
This checks which rows in column A from sheet 2 match A1. For those that do, it then checks which rows in column B from sheet 2 match B1. For those, it pulls the row number from that match. Everything else returns "". Assuming no duplicates, there should only 1 row number which gets returned. To pull that number from the array of results, wrap the whole thing in a MATCH function. Now that you have the row number, you can use an INDEX function to pull the result in Column C with that row, like this:
FINAL ARRAY FORMULA METHOD
=INDEX($C$1:$C$100,MAX(IF(Sheet2!$A$1:A$100=A1,IF(Sheet2!$B$1:$B$100,ROW(Sheet2!$B$1:$B$100),""),"")))
Remember to confirm with CTRL + SHIFT + ENTER instead of just ENTER, when you type this formula. Note that I didn't refer to all of Sheet2!A:A, because array formulas run very slowly over large ranges.
The following formula should work without making any changes to the datasheets.
=INDEX(Sheet2!$A$1:$A$360,MATCH(Sheet1!A1,IF(Sheet2!$C$1:$C$360=Sheet1!B1,Sheet2!$B$1:$B$360),0))
Remember to save this formula as an array with CTRL+SHIFT+ENTER
Documentation on how to use INDEX and MATCH against multiple criteria can be found on Microsoft Support.
It's not clear what you want to do with the multiples that do not have corresponding matches. txed is listed as Unreported twice in Sheet1; kntyctap is listed as Unreported three times. There are only one corresponding match on Sheet2 for each of these.
Non-array Standard Formulas for multiple criteria matches
For Excel 2010 and above use this standard formula in Sheet1!C1:
=IFERROR(INDEX(Sheet2!$A$1:$A$999,AGGREGATE(15,6,ROW(1:999)/((Sheet2!$B$1:$B$999=A2)*(Sheet2!$C$1:$C$999=B1)), COUNTIFS(A$1:A1, A1, B$1:B1, B1))), "")
For version of Excel prior to 2010 use this standard formula in Sheet1!C1:
=IFERROR(INDEX(Sheet2!$A$1:$A$999, SMALL(INDEX(ROW($1:$999)+((Sheet2!$B$1:$B$999<>A1)+(Sheet2!$C$1:$C$999<>B1))*1E+99, , ), COUNTIFS(A$1:A1, A1, B$1:B1, B1))), "")
I've handled error with the IFERROR function in that latter formula. Excel 2003 and previous may have to use an IF(ISERROR(..., ...)) combination.

Resources