Recently I was working on a bi-weekly task I’ve been doing recently for a group I’m part of ,Orthodox Tewahedo Youth of NYC. In this task, I needed to create pairings for our bi-weekly “Emmaus Walks” and it’s a bit annoying because you have to see who registered, pair people up as best as you can in twos, and make sure no one is paired with someone they’ve already been paired with. It’s all in WhatsApp, so it’s a bit of a hassle to do this manually. Lot of clicking around and jotting things down.
And as it always goes, if something annoys me or feels like a drag, I’ll try to automate as much of it as possible for as free as possible.
Technically you can copy paste the names into some AI tool or use a randomizer, but you still need to keep track of who has been paired with who in the past.
So here is what I needed:
- A private place to store the pairings. Only people in the group should be able to see it.
- A way to track who has been paired with who So we don’t pair people who have been paired before.
- A way to generate pairings. So we don’t have to do it manually.
I also didn’t want to pay a single dollar for this. So dug around as to what super dumb tools I could use to build this and learned about Google Apps Script.
It was perfect for my use case. I could use Google Sheets as both the tool and the database. Then use Google Apps Script to write a script that would generate the pairings.
Here’s how I built it:
The Engineering Synopsis
The Core Problem This is effectively a Constraint Satisfaction Problem (CSP). I had a set of variables (participants) that need to be assigned values (partners) subject to specific constraints:
- Exclusion Constraint: No pair $(A, B)$ can exist if $(A, B) \in \text{History}$.
- Uniqueness Constraint: Each participant $p_i$ must appear exactly once per session.
- Cardinality Constraint: Pairs are preferred, but if $N \pmod 2 \neq 0$, one group must be a triad $(A, B, C)$.
Architecture: “Serverless” Spreadsheets
Instead of a traditional MEN/MERN stack, I used the Google Workspace ecosystem:
- Database: A
Historysheet acting as an append-only log. - Frontend: A
Dashboardsheet with Data Validation (dropdowns) to enforce schema consistency (preventing typos like “Jon” vs “John”). - Backend: Google Apps Script (JavaScript) running on V8 runtime.
The Algorithm
I implemented a Backtracking (Depth-First Search) algorithm to solve the pairing. This approach is more algorithmically rigorous than a simple random shuffle and guarantees finding a valid solution if one exists.
- Data Structure:
- I load the entire historical dataset into a JavaScript
Setfor O(1) lookup time. - Inputs are sanitized (whitespace trimmed, duplicates removed).
- Recursive Backtracking:
- The algorithm attempts to build a valid set of pairings recursively.
- Step-by-step: It pairs Person A with Person B. Checks if valid (i.e., not in history).
- Progress: If valid, it moves to the next available person (Person C) and tries to pair them.
- Backtrack: If it reaches a dead end (e.g., Person C cannot be paired with anyone remaining without violating constraints), it “backtracks”: it un-pairs the previous couple and attempts a different combination.
- Systematic Exploration:
- Unlike a randomized approach which relies on “shuffling luck”, this method systematically explores the solution space.
- It ensures that if a valid configuration exists within the constraints, the script will find it.
Data Persistence
- Write Operations: Results are written to the Dashboard for display and appended to the History log with a batch timestamp.
- Transactional Integrity (Undo): The “Undo” feature performs a timestamp-based rollback, locating the specific batch ID (time) in the logs and removing only those entries to maintain database consistency.
Why do this?
It took an hour to write, costs zero dollars, and I can hand it off to anyone just by sharing the sheet. They just click buttons. Sometimes the best “app” is just a spreadsheet with some codes hooked up to it.
Of course it doesn’t probably doesn’t scale to a million users, nor are three buttons for undo, generate, and reset enough for a production app. But for a small group of people, it’s perfect. Made my life just a bit easier and I learned something new.