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