Archive for the ‘List and Table Functions’ Category.

Random Sorts

Red wants to have a kind-of lottery for his students. He will award them prizes based on a random drawing, but wants to weight each student based on the number of assignments turned in. Normally, I would accomplish this by typing the name of each student in column A one time for every assignment he turned in. If John turned in three assignments, I'd type his name three times. In column B, I'd put a RAND() function and fill it down. Then I'd sort by column B. I'd get something that looks like this:

That's all well and good, but it's missing a few things. If I'm giving away three prizes, Sue wins them all unless I manually exclude her. But the worst part is that it happens too fast. Excel calculates so fast that it's not entertaining to calculate in front of a group (unless you're Charles Williams, of course). I wanted to come up with something that doesn't allow ties, calculates more slowly, and is generally more friendly. Here's my first stab

Behind the button, I have this code:

Public Sub DrawNext()
   
    Dim rNames As Range, rCell As Range
    Dim rLastICell As Range
    Dim i As Long
   
    FillNames
   
    Set rLastICell = wshDraws.Range("I65536").End(xlUp).Offset(1, 0)
   
    If rLastICell.Row> 2 Then
        Set rLastICell = rLastICell.Offset(-1)
       
        Set rNames = wshDraws.Range("I2", rLastICell)
       
        For Each rCell In rNames.Cells
            For i = 1 To 50
                rCell.Offset(0, 1).Value = Rnd * 1000
            Next i
        Next rCell
    End If
   
End Sub
 
Private Sub FillNames()
   
    Dim rNames As Range
    Dim rCell As Range, rEntry As Range
    Dim i As Long, j As Long
   
    Set rNames = wshDraws.Range("I2")
    Set rEntry = wshDraws.Range("A2:A31")
   
    wshDraws.Range("I2:J65536").ClearContents
    j = 0
   
    For Each rCell In rEntry.Cells
        If Not IsEmpty(rCell.Value) Then
            For i = 1 To rCell.Offset(0, 1).Value
                rNames.Offset(j).Value = rCell.Value
                j = j + 1
            Next i
        End If
    Next rCell
   
End Sub

The user enters the information in columns A and B, up to 30 students. The code fills column I with one instance of each name for each assignment. Then for each name it fills in a random number between 0 and 999. For show, it fills each cell 50 times to make it look like it's really doing some work.

In column C, I have this array formula

=MAX((rNames=A2)*(rDraws))

and in column D, this

=IF(RANK(C2,$C$2:$C$31)>3,"",CHOOSE(RANK(C2,$C$2:$C$31),"1st Place","2nd Place","3rd Place"))

There's not a lot of error checking and far too many literals in the code, but it's a start.

Download Lotter.zip. Yes, it's 2003 format.

Working with Data Tables in Excel 2007

Hi all,

Today I published my first article dedicated to Excel 2007 on my site.

The article is about Excel 2007's new Table feature and is aimed at the beginner Excel 2007 user (that would be just about everyone except John Walkenbach :-) ).

From the intro:

With the release of Excel 2007, Microsoft has introduced a new concept of working with tables of data. This new functionality is (not surprisingly) called “Tables”.
This article introduces you into the concepts of working with Tables and shows you how they may help you in your everyday Excel use.

Tables in Excel 2007 feature:

Integrated autofilter and sort functionality
Easy selecting
Header row remains visible whilst scrolling
Automatic expansion of table
Automatic reformatting
Automatic adjustment of charts and other objects source range

Read on here...

### Edited to correct the name of the feature ###

Enjoy!

Regards,

Jan Karel Pieterse
www.jkp-ads.com

Calculating Running Totals without Errors

I use Excel to maintain my projected personal and corporate cash flows based on my current balance and projected income and expenses throughout the month.

Initial cash flow worksheet

Column B contains income, column C contains expenses, and column E contains my running total. When I first created my worksheet, I used formulas such as =E4 + B5 - C5 to calculate the running balance (in this case for cell E5) after figuring in some income or an expense. The problem came when I deleted a row. For example, assume that I deleted the 8/2/2006 transaction from the list in the previous graphic.

Deleting a row with a formula causes #REF! errors.

When you delete a cell referred to in a formula, Excel displays the #REF! (bad reference) error code. Editing the formula in the first error cell causes Excel to fix the problem in every affected cell, but there is a way to avoid the errors entirely. To do so, create an OFFSET function to look up the previous balance, income (if any), and expense (if any). Here's the formula I use for cell E5:

=OFFSET(E5,-1,0)+OFFSET(E5,0,-3)-OFFSET(E5,0,-2)

Managing running totals and balances using Excel 2003 data lists, or Excel 97-2002 worksheets, is somewhat involved. You can find a free article documenting the process on my web site.

All the best.

Curt

Two Variable Lookups

Tomkat is trying to cap mileage paid based on production. The mileage cap varies based on production and site. Read this newsgroup post for details if you like. This newsgroup post is where it all started.

This calls for a VLOOKUP/MATCH combo if I ever saw it.

img: table of mileage rates

The formula in E15 (and filled down column E) is

=MIN(D15,VLOOKUP(B15,$A$2:$E$11,MATCH(C15,$C$1:$E$1,FALSE)+2))

MATCH: The MATCH function returns the column that matches the site entered. It's used as the col_index_num argument of VLOOKUP, that is, which column VLOOKUP retrieves. You have to add two to the result because the range of "Sites" starts two cell right of the vlookup range.

VLOOKUP: The left-most column of the mileage cap table is filled with the lowest number of the appropriate range of numbers. Column B shows the range of numbers, but it's just there for informational purposes. Because there is no fourth argument in the VLOOKUP, it will find the largest number that is not greater than the number it's looking for. If, as in row 15, we're looking for 342, it will find 300 because that's the largest number in the list that's not greater than 342.

MIN: The whole thing is wrapped in a MIN because it's a cap. If the person claims less than the cap, then that value should be used, not the cap.

I hope that helps Tomkat.

VLOOKUP and INDIRECT

The INDIRECT function can be used to return a range to the VLOOKUP formula. Andy provides this example where the sheet is different for every row, but he doesn't want to change the formula manually.

vlookup formula using indirect

I could see this being useful when the lookup value determines which page the data is on. For example, if you had your inventory on several sheets in a workbook and the inventory item number told you which page it was on. A furniture manufacturer has two classes of inventory, desks and chairs. All the desks start with 'DK' and the chairs start with 'CR'. He could still keep his inventory on two pages and use INDIRECT to point his VLOOKUP to the right list.

vlookup formula using indirect

range of desk inventory items

With only two inventory classes, there may be a simpler way to do this, but if there were a dozen, this would be a nice shortcut.

Golf Handicap

The USGA has a formula to compute your handicap and the people who compute my handicap, GHIN, only do so about twice per month. I have no idea why it's so infrequently, but they probably have their reasons. In the meantime, I'm trying to replicate the formula in Excel.

range showing score history

The formulas are:

$D$5  =TRUNC((SUMIF(HistUsed,"?",HistDiff)/COUNTIF(HistUsed,"?"))*0.96,1)
$G$5  =ROUND(D5/BOGEYSLOPE*F5,0)
$C$9  =IF(OR(D9="",D9<LARGE(HistDate,MIN(20,COUNT(HistDate)))),"",
      IF(H9<=SMALL(((HistDiff)*IF((HistDate>=LARGE(HistDate,MIN(20,COUNT(HistDate)))),1,1000)),
      VLOOKUP(COUNT(HistDate),ScoresUsed,2)),"*",""))
$H$9  =IF(D9="","",ROUND((E9-F9)*BOGEYSLOPE/G9,1))

The defined names are:

BOGEYSLOPE  =113
HistDate  =OFFSET(ScoreHistory!$D$8,1,0,COUNT(ScoreHistory!$D$9:$D$64987),1)
HistDiff  =OFFSET(ScoreHistory!$H$8,1,0,COUNT(ScoreHistory!$D$9:$D$64987),1)
HistUsed  =OFFSET(ScoreHistory!$C$8,1,0,COUNT(ScoreHistory!$D$9:$D$64987),1)
ScoresUsed  =Lists!$A$1:$B$11

There are some things missing from this calculation, including 9-hole scores and tournament scores, but it's a start. I haven't had the opportunity to check it against the GHIN's calc yet.

In the D5 formula, I had to SUMIF but I was looking for a wildcard character, namely the asterisk. I don't know how to SUMIF for an asterisk. Since that column can only have an asterisk or an empty cell, I used the question mark to essentially determine if it had a one character string in it or not. I was pretty proud of that, but I still would like to know how to do it right.

I'm going to try to break down the C9 formula in English.

1 =IF(
2  OR(
3   D9="",
4   D9<LARGE(
5    HistDate,
6    MIN(
7     20,
8     COUNT(HistDate)
9    )
10   )
11  ),
12 "",
13 IF(
14  H9<=SMALL(
15   ((HistDiff)*IF(
16    (HistDate>=LARGE(
17     HistDate,
18     MIN(20,COUNT(HistDate))
19    )),
20    1,
21    1000)),
22   VLOOKUP(
23    COUNT(HistDate),
24    ScoresUsed,
25    2
26   )),
27  "*",
28  ""))

Boy, that was a lot of work and it's not any easier to follow. Oh well.

Line 1-12: If there is no date or the date is not one of the 20 most recent dates, then return an empty string.
Line 4-8: Find the 20th largest date (or a less than the 20th) and compare that to the date.
Line 6-8: If there are less than 20 entries, then include all the dates, otherwise just the most recent 20.
Line 13: This starts the Else from Line 1. If the date isn't blank AND the date is within the last 20 dates, then put an asterisks in the cell if the differential is one of the smallest differentials.
Line 22: The smallest differentials will generally be the smallest 10 of the last 20, but if there's less than 20 scores, the number is different. It's defined in the ScoresUsed defined name and this looks it up.
Line 14-21: If this differential is less than the xth smallest differential... I start with all the differentials but I need to exclude any that are older than 20 scores old. Normally, you can use an array formula to make those score zero, but I'm in the middle of a SMALL function and I would essentially be making those the smallest (at zero) which screws everything up. Instead, I put an IF in there. If the date is too old, I multiply the differential by 1000 instead of by 1, which will make it so large that it won't be a factor in the SMALL function.

Clear as mud, huh? Remind me never to explain formulas again.

Update: Because WordPress screws up the quotes, some people are having trouble recreating this worksheet. You can download the workbook Handicap.zip.

Update: I fixed the above download to 1) Handle ties in the differential 2) Handle ties in the date and 3) Protect the formulas.