Bourbon Ranker – Annual Rankings
body {
font-family: Arial, sans-serif;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
padding: 8px 10px;
border-bottom: 1px solid #ddd;
}
th {
cursor: pointer;
background: #f4f4f4;
text-align: left;
}
th:hover {
background: #eaeaea;
}
.rank-change.up { color: green; }
.rank-change.down { color: red; }
.rank-change.new { font-weight: bold; }
.bottle-name {
font-weight: bold;
}
.distillery {
font-size: 0.9em;
color: #555;
}
#controls {
margin-bottom: 10px;
}
#summary {
margin-top: 15px;
padding: 10px;
border: 1px solid #ddd;
background: #fafafa;
}
Year:
| Year |
Rank |
Bottle |
Change in Rank |
Summary
/* ======================
DATA SECTION
====================== */
const rankings = [
{
year: 2023,
rank: 1,
name: “Pappy Van Winkle’s Family Reserve 23 Year”,
distillery: “Old Rip Van Winkle”,
singleBarrel: false
},
{
year: 2023,
rank: 2,
name: “George T. Stagg”,
distillery: “Buffalo Trace”,
singleBarrel: false
},
{
year: 2024,
rank: 1,
name: “George T. Stagg”,
distillery: “Buffalo Trace”,
singleBarrel: false
},
{
year: 2024,
rank: 2,
name: “Pappy Van Winkle’s Family Reserve 23 Year”,
distillery: “Old Rip Van Winkle”,
singleBarrel: false
}
];
/* ======================
LOGIC
====================== */
const normalize = str =>
str.toLowerCase().replace(/\b(batch|year|\d{4}|\d+ yr)\b/g, ”).trim();
const years = […new Set(rankings.map(r => r.year))].sort();
const yearFilter = document.getElementById(“yearFilter”);
const tbody = document.querySelector(“#rankingTable tbody”);
const searchInput = document.getElementById(“search”);
years.forEach(y => {
const opt = document.createElement(“option”);
opt.value = y;
opt.textContent = y;
yearFilter.appendChild(opt);
});
yearFilter.value = Math.max(…years);
function getChange(entry) {
const prev = rankings.find(r =>
r.year === entry.year – 1 &&
normalize(r.name) === normalize(entry.name)
);
if (!prev) return “NEW”;
const diff = prev.rank – entry.rank;
return diff === 0 ? “—” : (diff > 0 ? `+${diff}` : diff);
}
function render() {
tbody.innerHTML = “”;
const year = Number(yearFilter.value);
const term = searchInput.value.toLowerCase();
const filtered = rankings
.filter(r => r.year === year)
.filter(r =>
r.name.toLowerCase().includes(term) ||
r.distillery.toLowerCase().includes(term)
)
.sort((a,b) => a.rank – b.rank);
filtered.forEach(r => {
const change = getChange(r);
const tr = document.createElement(“tr”);
tr.innerHTML = `
${r.year} |
${r.rank} |
${r.name}
${r.distillery}
|
${change}
|
`;
tbody.appendChild(tr);
});
renderSummary(year);
}
function renderSummary(year) {
const current = rankings.filter(r => r.year === year);
const previous = rankings.filter(r => r.year === year – 1);
const recurring = current.filter(c =>
previous.some(p => normalize(p.name) === normalize(c.name))
);
const obsoleteSingleBarrels = previous.filter(p =>
p.singleBarrel &&
!current.some(c => normalize(c.name) === normalize(p.name))
);
document.getElementById(“summaryText”).innerText =
`${current.length} total bottles ranked in ${year}. ` +
`${recurring.length} returned from ${year – 1}. ` +
`${current.length – recurring.length} were new entries. ` +
`${obsoleteSingleBarrels.length} single barrel releases from ${year – 1} did not return and are now considered obsolete.`;
}
yearFilter.addEventListener(“change”, render);
searchInput.addEventListener(“input”, render);
render();
Leave a comment