Top ten ordering in Excel based on complex team rules - excel

I have an excel spreadsheet in a format similar to the following...
| NAME | CLUB | STATUS | SCORE |
| Fred | a | Gent | 145 |
| Bert | a | Gent | 150 |
| Harry | a | Gent | 195 |
| Jim | a | Gent | 150 |
| Clare | a | Lady | 99 |
| Simon | a | Junior | 130 |
| John | b | Junior | 130 |
:
:
| Henry | z | Gent | 200 |
I need to convert this table into a list of the "Top Ten" teams. The rules are
Each team score is taken from the sum of four members of that club.
These totals should be of the best four scores except...
Each team must consist of at least one Junior or Lady
For example in the table above the team score for club A would be 625 not 640 as you would take the scores for Harry(190), Bert(150), Jim(150), and Simon(130). You could not take Fred's(145) score as that would give you only Gents.
My question is, can this be done easily as a series of Excel formula, or will I need to resort to using something more procedural?
Ideally the solution needs to be automatic in the team selections, I don't want to have to create separate hand crafted formula for each team. I also will not necessarily have a neatly ordered list of each clubs members. Although I could probably generate the list via an extra calculation sheet.

Public Function TopTen(Club As String, Scores As Range)
Dim i As Long
Dim vaScores As Variant
Dim bLady As Boolean
Dim lCnt As Long
Dim lTotal As Long
vaScores = FilterOnClub(Scores.Value, Club)
vaScores = SortOnScore(vaScores)
For i = LBound(vaScores, 2) To UBound(vaScores, 2)
If lCnt = 3 And Not bLady Then
If vaScores(3, i) <> "Gent" Then
lTotal = lTotal + vaScores(4, i)
bLady = True
lCnt = lCnt + 1
End If
Else
lTotal = lTotal + vaScores(4, i)
lCnt = lCnt + 1
If vaScores(3, i) <> "Gent" Then bLady = True
End If
If lCnt = 4 Then Exit For
Next i
TopTen = lTotal
End Function
Private Function FilterOnClub(vaScores As Variant, sClub As String) As Variant
Dim i As Long, j As Long
Dim aTemp() As Variant
For i = LBound(vaScores, 1) To UBound(vaScores, 1)
If vaScores(i, 2) = sClub Then
j = j + 1
ReDim Preserve aTemp(1 To 4, 1 To j)
aTemp(1, j) = vaScores(i, 1)
aTemp(2, j) = vaScores(i, 2)
aTemp(3, j) = vaScores(i, 3)
aTemp(4, j) = vaScores(i, 4)
End If
Next i
FilterOnClub = aTemp
End Function
Private Function SortOnScore(vaScores As Variant) As Variant
Dim i As Long, j As Long, k As Long
Dim aTemp(1 To 4) As Variant
For i = 1 To UBound(vaScores, 2) - 1
For j = i To UBound(vaScores, 2)
If vaScores(4, i) < vaScores(4, j) Then
For k = 1 To 4
aTemp(k) = vaScores(k, j)
vaScores(k, j) = vaScores(k, i)
vaScores(k, i) = aTemp(k)
Next k
End If
Next j
Next i
SortOnScore = vaScores
End Function
Use as =TopTen(H2,$B$2:$E$30) where H2 contains the club letter.

can this be done easily as a series of
Excel formula
Short answer, YES. (Depending on your definition of "easily").
Long answer...
(I think this works)
Here's my (brief) test data:
A B C D
1 NAME CLUB STATUS SCORE
2 Kevin a Gent 145
3 Lyle a Gent 150
4 Martin a Gent 195
5 Norm a Gent 150
6 Oonagh a Lady 100
7 Arthur b Gent 200
8 Brian b Gent 210
9 Charlie b Gent 190
10 Donald b Gent 220
11 Eddie b Junior 150
12 Quentin c Gent 145
13 Ryan c Gent 150
14 Sheila c Lady 195
15 Trevor c Gent 150
16 Ursula c Junior 200
Now, if I've understood the rules correctly, we want the best four scores, except that if the highest score by either a lady or a junior is not in the best four, we use that instead of the fourth highest. I've restated it somewhat, for reasons that may become apparent...
OK. Array formulae to the rescue! (I hope)
The highest score from team a should be
{=LARGE(IF(B2:B16="a",D2:D16,0),1)}
where the {} indicates an array formula created by using Control-Shift-Enter to input the formula. The top four are similarly created. For the Lady/Junior bit, we need a bit more complexity. Taking the Lady, we need this:
{=LARGE(IF($B$2:$B$16=$J3,IF($C$2:$C$16="Lady",$D$2:$D$16,0),0),1)}
Junior may safely be left as an exercise for the student, I hope.
I'm now looking at a table with the following layout for club "a"
J K L M N O P
1 Club 1 2 3 4 Lady Junior
2 a 195 150 150 145 100 0
The club score should be the top three "anyone" scores plus the best lady or junior if they're not already in the top four.
So in Q2 I'm putting this:
=SUM(K2:M2)+MIN(MAX(O2,P2),N2)
MAX(O2,P2) tells me the best lady or junior score, which has to be included. If it's higher than the fourth-highest team score, then it's already in the list and we just take the top four. Otherwise, we replace the fourth-highest score with the best lady/junior one.
Now we could do it all in one formula, by substituting the parts into the final formula:
{=LARGE(IF($B$2:$B$16=$J3,$D$2:$D$16,0),1)+
LARGE(IF($B$2:$B$16=$J3,$D$2:$D$16,0),2)+
LARGE(IF($B$2:$B$16=$J3,$D$2:$D$16,0),3)+
MIN(LARGE(IF($B$2:$B$16=$J3,$D$2:$D$16,0),4),
MAX(LARGE(IF($B$2:$B$18=$J3,IF($C$2:$C$18="Lady",$D$2:$D$18,0),0),1),
LARGE(IF($B$2:$B$18=$J3,IF($C$2:$C$18="Junior",$D$2:$D$18,0),0),1)))}
But I don't recommend it...
So for the above data, I end up with this:
Anyone Lady Junior
Club 1 2 3 4 1 1 Total
a 195 150 150 145 100 0 595
b 220 210 200 190 0 150 780
c 200 195 150 150 195 200 695
Rats. In my excitement at (I think) getting the hard part to work I forgot to mention that
The list of scores can be in any order
You can get the club rankings with RANK()
You can then pull the top 10 into another table using MATCH() and INDEX()
A B C D E F G H
1 club Sc Rank UniqRk Pos Club Score
2 third-equal#1 80 3 79.999980 1 1 best 100
3 second 90 2 89.999970 2 2 second 90
4 third-equal#2 80 3 79.999960 3 3 third-equal#1 80
5 best 100 1 99.999950 4 3 third-equal#2 80
6 worst 70 5 69.999940 5 5 worst 70
Columns A and B are our calculated scores, column E is the order in which clubs will be output in the final table. The other formulae are as follows:
C: =RANK(B2,$B$2:$B$6) # what it says, with ties both getting the lower number
D: =B2-ROW()*0.00001 # score, modified slightly to ensure uniqueness
F: =SMALL($C$2:$C$6,E2) # first output column, ranks including ties
G: =INDEX($A$2:$A$6,MATCH(LARGE($D$2:$D$6,E2),$D$2:$D$6,0))
# club name for position, using the modified score in D
H: =INDEX($B$2:$B$6,MATCH(LARGE($D$2:$D$6,E2),$D$2:$D$6,0))
# as G, but indexes into scores

What I do is lame, but it works.
Just make a new column then insert this formula =If(a1=N,b1,0) where A1 is criteria column, N is criteria and B1 is in the column that you are trying to get the large from. Then I just do the large formula in another column.
Sometimes I get all fancy and instead of rolling out a N, I will make it say $C$1, then spell out the criteria in that cell.
The perfect answer would be to have Microsoft add in a largeifs (please read this Microsoft)

Writing a solution in VBA would be my first choice, especially if the rules have the possibility of becoming more complex.

Use a pivot table which will act as a database query on the data you have. Pivot so that the teams go down the columns and team members along with their status type go across the pivot table. I'm not sure for 2003, but Excel 2007 lets you then sort so the highest scores appear to the left. Then your first sum can simply take the first three scores for the each team. However to get the last persons sum, you have to determine if you can use the 4th score, or if you have to use the max of the junior or Lady types. That could be done using a complex and brute force formula somewhat like this:
if (type of position 1 is a junior or a lady or ... 2 or 3... ) then use position 4 else if position 5 is a junior or lady then use 5 else if p 6 is ... and so on.

I don't think that this can be done unless the table is sorted in some way. Most of Excel's lookup functions require ordered lists. This could certainly be done with a VBA function.

Related

Spreadsheet - Im trying to find the sum of the top n numbers in two columns

-UPDATED- Answered, thanks for all who helped.
Consider the following Google spreadsheet:
A B C D E
1 John | Bob | Sue | Tony
2 h1 2 | 1 | 3 | 2
3 h2 3 | 3 | 4 | 2
4 h3 1 | 2 | 1 | 3
5 h4 2 | 2 | 3 | 1
6 h5 2 | 1 | 1 | 3
7 h6 1 | 2 | 2 | 1
8 h7 1 | 2 | 1 | 3
Team | Player1 | Player 2 | Score
1 | John | Sue | ?
2 | Bob | Tony | ?
Each team is made of two partners, e.g. John and Sue. Each row contains a match: the team's score is the best of each member's. The team total score of the game is the sum of the match scores.
In the example:
Team 1 : John & Sue. Match scores: (3,4,1,3,2,2,1). Total score = 16.
Team 2 : Bob & Tony. Match scores: (2,3,3,2,3,2,3). Total score = 18.
Another example would be two golfers working as team and the best score between them is counted per hole, the at the end we add those up.
Can this be done using a spreadsheet?
To get the desired result, the formula comes up quite complicated:
=SUMPRODUCT(IF(MMULT((B12=$B$1:$E$1)*$B$2:$E$8,ROW(A1:A4)^0)>MMULT((C12=$B$1:$E$1)*$B$2:$E$8,ROW(A1:A4)^0),(B12=$B$1:$E$1)*$B$2:$E$8,(C12=$B$1:$E$1)*$B$2:$E$8))
but works in both Excel and GS
In Excel you can use the LARGE() function. It is the easiest option but a bit verbose.
If you want to the sum of the top 3 values in a column/row:
= large(A1:A10, 1), large(A1:A10, 2) + large(A1:A10, 3)
In Excel
If one has the new dynamic array formula LET():
=LET(x,INDEX($B$2:$E$8,0,MATCH(I2,$B$1:$E$1,0)),y,INDEX($B$2:$E$8,0,MATCH(J2,$B$1:$E$1,0)),SUMPRODUCT(((x>y)*x)+((y>=x)*(y))))
Else
=SUMPRODUCT(((INDEX($B$2:$E$8,0,MATCH(I2,$B$1:$E$1,0))>INDEX($B$2:$E$8,0,MATCH(J2,$B$1:$E$1,0)))*INDEX($B$2:$E$8,0,MATCH(I2,$B$1:$E$1,0)))+((INDEX($B$2:$E$8,0,MATCH(J2,$B$1:$E$1,0))>=INDEX($B$2:$E$8,0,MATCH(I2,$B$1:$E$1,0)))*(INDEX($B$2:$E$8,0,MATCH(J2,$B$1:$E$1,0)))))
Either of the following formulas will produce the desired sums of 16 and 18 (tested on my machine):
=ArrayFormula(SUM(IF($B$2:$B$8>$D$2:$D$8,$B$2:$B$8,$D$2:$D$8)))
=SUMPRODUCT(IF($B$2:$B$8>$D$2:$D$8,$B$2:$B$8,$D$2:$D$8))
Adjust to B->C and D->E for Bob + Tony. These formulas work by operating on arrays. They evaluate the IF statement once per cell in the B2:B8 range and generate an array of values ({3,4,1,3,2,2,1}). Then SUM or SUMPRODUCT will sum those values. ArrayFormula is necessary to force SUM to deal with the IF as an array.
Further customization can be built from here as desired. Play around with ArrayFormula and SUMPRODUCT as they have much more powerful use cases than this and have parallels in other spreadsheet softwares including Excel.

Tracking work towards awards in MS Excel

I’m trying to create a report from existing data about tasks people have completed and whether they can give given awards for completion of the tasks
I have a list of people who have been working to complete tasks in an exhaustive list, completion of which is recorded in an Excel sheet with ticks or similar characters. (table 1.)
For each of these people, awards can be given when various subsets of these tasks are completed. The tasks required for these awards overlap and are maintained in a list per award. (table 2.)
I would like to generate a report which identifies for each person how many tasks he or she has completed toward each award. I will use this report to identify if they can be given the award or how many more tasks they need to complete.
One way to do it... If you change the tick marks to a numeric value of one (1), you can add formulas to additional columns on the table (or a separate sheet).
| A B C D E F G H I J K L M N
+----+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+----------+----------+----------+
1 | task01 task02 task03 task04 task05 task06 task07 task08 task09 task10 Award 01 Award 02 Award 03
2 | name01 1 1 1 1 1 1 Yes No No
3 | name02 1 1 1 No No No
4 | name03 1 1 1 1 1 1 1 No Yes No
5 | name04 1 1 No No No
6 | name05 1 1 1 1 No No No
7 | name06 1 1 1 No No No
8 | name07 1 1 1 1 1 1 1 1 No Yes Yes
9 | name08 1 1 No No No
10 | name09 1 No No No
11 | name10 1 No No No
The formulas used in L2, M2, and N2:
=IF(SUM(B2:F2)=5,"Yes","No")
=IF(SUM(G2:K2)=5,"Yes","No")
=IF(SUM(C2,F2,I2,J2,K2)=5,"Yes","No")

Get a Sum of ONLY duplicate values

If there a way to use a sumif combined with a countif?
for example if I have
TransactionNo|NumberToSum
1 |
1 | 9
2 | 6
3 | 3
4 |
4 | 4
5 | 6
6 | 4
7 |
7 | 9
8 | 7
9 | 4
10 |
10 | 1
11 | 3
12 | 6
My result would be: 23
because the values 1,4,7,10 are duplicated, so I need to sum the NumberToSum of those 9,4,9,1 in this case.
Ideally I would like somethings along the lines of =SumIf(A:A,Count>1,B:B)
Where B is summed if the count of A inside of A:A is > 1.
but =SUMIF(A:A,COUNT(A:A)>1,B:B) does not work.
NOTE: the Data is NOT sorted as it appears in my example.
If I wanted to do it in VBA one quick way could be:
Sub sumdups()
Dim rng As Range
Dim rng2 As Range
Dim Sum As Long
Set rng = [A2:A17]
For Each rng2 In rng
If WorksheetFunction.CountIf(Range("A1:A17"), rng2.Value) > 1 Then
Sum = Sum + rng2.Offset(0, 1).Value
End If
Next rng2
End Sub
Another option I have tried is to add a column with the formula
=IF(COUNTIF(A:A,A2)>1,B2,"")
Then sum the helper column, but I am hoping there is a simple formula that can do the same all in one cell.
I am really just looking for a faster formula that can be entered in a single cell, without the need for any additional calculations.
This formula works for me:
=SUMPRODUCT(1*(COUNTIF(A1:A100,A1:A100)>1),(B1:B100))
or for entire column (a little bit slower):
=SUMPRODUCT(1*(COUNTIF(A:A,A:A)>1),(B:B))

count data using two columns as references

Is it possible to count or countif by using a column as the data, a cell for the criteria (or what to match) and range of what to count?
Here is what I am looking at:
A1 B C D E F G H I J K L M N O
2 Running Data Total Count of Tardies (by category)
3 Date Employees Leader Start of Shift Break 1 Lunch Break 2 Employees Start of Shift Break 1 Lunch Break 2 Total
4 1-Jul Abe Sue 15 Abe 0
5 3-Jul Steve Bob 20 Anna 0
6 5-Jul Eve Andy 9 20 Eve 0
7 7-Jul Anna Andy 30 Helen 0
8 15-Jul Abe Sue 15 Mark 0
9 18-Jul Anna Andy 10 Steve 0
10 20-Jul Helen Sue 9 0
11 31-Jul Mark Bob 45 0
I am trying to count the data entered on the left (running data) in each category and having it show based on the Employees on the right (in the orange cells). So Abe should show 1 for Start of Shift, Eve should show 1 for Break 1 and Break 2, and Anna should show 2 for Start of Shift.
I have tried using:
=countif(C:C,$J4,D:D) to get the data from JUST Column D for Start of shift, but it gives and error saying too many arguments for the function have been entered.
Help...
...and Thanks!
Countif will only look at 1 column to decide what to count.
Countifs will look at multiple columns. Your formula would look something like this:
=COUNTIFS($C:$C,$J4,E:E,">0")

Excel to calculate if values in ranges meet certain criteria using VBA

I have two ranges in excel, say:
x | y
------------
5 | -1
46 | -4
2 | 1
67 | -1
22 | 1
6 | 0
34 | 0
7 | -2
I want calculate the sum of the second column for values less than O only if the respective values in the first column is less than 10 (i.e sum(y(i) for i<0 and x(i)<10) . Hence in this case the sum will be -3.
Assuming your headers are in A1:B1 and your data is A2:B9 use this:
=SUMIFS(B2:B9,A2:A9,"<10",B2:B9,"<0")
Try something like
Function calc() AS Integer
Dim sum AS Integer: sum = 0
Dim c AS Range
For Each c In ThisWorkbook.Worksheets(1).Range("A1:A15")
If c.Value < 10 And c.Offset(0, 1).Value < 0 Then
sum = sum + c.Offset(0, 1).Value
End If
Next c
calc = sum
End Function

Resources