Amy Whitehead's Research

the ecological musings of a conservation biologist

Santa’s Little Helper – a function for randomly assigning Secret Santa presents


Christmas can be a stressful and expensive time of year, with many gifts to buy for many people. My family have tried to eliminate some of this stress by having a Secret Santa gifting system where we all buy one present for one member of the family on behalf of everybody else, with a cap of $50 per present.

In our family scheme, there are 3 rules:
1. You can’t give a gift to yourself,
2. You can’t give a gift to your partner (we figure that this will happen anyway),
3. You can’t give a gift to someone you gave one to last year.

Somewhere along the line, I’ve become the designated present-assigner. Now I could write names on pieces of paper and put them in a hat but 1) writing out 11 names is tedious, 2) I don’t have a suitable hat and 3) the rules above mean that there are quite often gifting conflicts arise that require the need for a redraw. So I’ve whipped up a little function in R that does the job. (Admittedly it probably would have been faster to use bits of paper but now I’m prepared for next year).

SantasLittleHelper <- function(myFrame,guestList,conflictCols = NULL){
  myTest <- TRUE
  nElves <- 0

  while (myTest == TRUE){
    myOut <- data.frame(gifter = myFrame[,guestList],
                        giftee = sample(myFrame[,guestList],
                                        replace = FALSE,

    # check that guests haven't drawn themselves
    guestTest<- unlist(lapply(1:nrow(myOut),function(x) {
      myOut$giftee[x] == myFrame[x,guestList]

    # check for gifting conflicts

    conflictTest <- unlist(lapply(1:nrow(myOut),function(x) {

    myTest <- any(c(guestTest,conflictTest[!]))

    } else{
      myTest <- any(guestTest)

    # count the number of iterations needed to avoid conflicts
    nElves <- nElves + 1

  message(paste(nElves,"elves were needed to generate the gift list"))

SantasLittleHelper takes three arguments: myFrame is a dataframe containing the list of guests to be assigned gifts; GuestList is the character name of the column containing the list of guests; and conflictCols is an optional character vector of column names that identify gifting conflicts that need to be avoided [1]. The function randomly assigns a giftee to each guest and then checks for conflicts. If conflicts exist, the gifts are reassigned and this continues using a while loop until a suitable solution is found. The function returns a dataframe containing detailing the gifter – giftee combinations, as well as outputting the number of elves (or iterations) needed to generate the list.

To run this for the 2016 Whitehead Family Christmas, I generated a dataframe guests where the column guest lists all of the people coming to Christmas, partner identifies everybody’s significant others and presents2013 lists the assigned giftees from 2013 (the last time we did the Secret Santa thing).

guest partner presents2013
John Fay Jo
Sue Simon Fay
Amy Hamish Sue
Jo Phil Hamish
Ashley Naomi Phil
Naomi Ashley John
Phil Jo Simon
Hamish Amy Naomi
Simon Sue Ashley
Fay John Amy

These data are fed into SantasLittleHelper and, bingo, the Whitehead Family Christmas is sorted for another year.

giftList <- SantasLittleHelper(myFrame = guests,
                               guestList = "guest",
                               conflictCols  = c("partner","presents2013")
27 elves were needed to generate the gift list
gifter giftee
John Zac
Sue Ashley
Amy Phil
Jo Amy
Ashley Fay
Naomi Sue
Phil John
Hamish Jo
Simon Naomi
Fay Simon
Zac Hamish

Hope everyone survives the silly season!

[Merry Christmas everyone!](

[1] Technically you could use this feature to rig the outcome to avoid getting a present from Great Aunt Myrtle or Bob from Accounts by adding in an additional conflict column. Use your powers wisely or the ghosts of christmas past may come back to bite you!

5 thoughts on “Santa’s Little Helper – a function for randomly assigning Secret Santa presents

  1. Hi Amy,

    Have you seen Hannah Fry’s latest video on secret santa selection? I think some of her points pertain to your implementation; namely that by using purely random selection and redrawing when conflictions occur you actually bias certain paths down the probability tree as others can not be valid, this leads to the pairing being non-uniform (i.e. some pairs are more likely than others).

    Not trying to be critical, it’s a neat function, just thought you might be interested 🙂

    Happy holidays!

  2. This is great! Thanks for sharing!

  3. Two adjustments to the code:


    This code worked fine for my first year with just one conflict column for guests’ partners, but when I added a second conflict column for to prevent a repeat giftee for the same gifter, I was getting some repeat gifter-giftee pairs. Changing one line of code for the conflictTest fixed the issue, so no one gave to their partner or to the same giftee from the previous year. The change was FROM “grepl(myOut$giftee[x],myFrame[x,conflictCols])” TO “grepl(as.vector(myOut$giftee[x]),unlist(myFrame[x,conflictCols])) “.


    Another addition I made was a setup to keep me blinded to all gifter-giftee pairs (except my giftee). I did this by creating a text file for each gifter. The file name lists the gifter, and once the file is opened, the name of the giftee is given. I emailed the gifters each their own file without opening it myself. I have the SantasLittleHelper function create an object with the current year in the object name, and have each text file include the year as well as the gifter in the file name. Here is the code calling Amy’s function and then creating the text files to be emailed to the gifters.

    giftList2019 <- SantasLittleHelper(myFrame = guests,
    guestList = "guest",
    conflictCols = c("partner", "lastyeargiftee")

    apply(giftList2019, 1, function(giftPair) {
    write.table(x = paste0("This year you are playing Santa for ", as.character(giftPair[1]), "."),
    file = paste0(giftPair[2], "_Christmas_2019", ".txt"),
    quote = FALSE,
    row.names = FALSE,
    col.names = FALSE

    Amy, thanks for posting your original code.


    • Thanks Troy for your comments and suggestions! This year I set mine up to email automatically using gmailr so that I didn’t know the gifter-gftee pairs. But your suggestion is also a great one. Merry belated Christmas!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s