Skip to content

Instantly share code, notes, and snippets.

@pgbovine
Last active May 9, 2019 16:28
Show Gist options
  • Save pgbovine/4a35dd3ccad99992afbe to your computer and use it in GitHub Desktop.
Save pgbovine/4a35dd3ccad99992afbe to your computer and use it in GitHub Desktop.
d3 sorting example
<!DOCTYPE html>
<html>
<head>
<title>6.MITx d3 activity - MIT dorm pressure survey - sorting demo</title>
<style>
.chart rect {
fill: steelblue;
}
.chart text {
fill: white;
font: 10px sans-serif;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Data source:
// http://tech.mit.edu/V132/N59/pressure/breakdown/residence/index.htm
//
// These attributes represent the percentage of YES answers for each dorm
// - happierThanAvg: "I am happier than the average MIT student."
// - extroverted: "Are you extroverted?"
// - workLifeBalance: "I strike a good balance between my personal life and work."
var mitPressureSurvey = [
{dorm: 'Baker House', happierThanAvg: 49, extroverted: 44, workLifeBalance: 54},
{dorm: 'Bexley Hall', happierThanAvg: 36, extroverted: 41, workLifeBalance: 38},
{dorm: 'Burton Connor', happierThanAvg: 50, extroverted: 39, workLifeBalance: 43},
{dorm: 'East Campus', happierThanAvg: 46, extroverted: 31, workLifeBalance: 46},
{dorm: 'MacGregor House', happierThanAvg: 42, extroverted: 25, workLifeBalance: 49},
{dorm: 'Maseeh Hall', happierThanAvg: 45, extroverted: 38, workLifeBalance: 43},
{dorm: 'McCormick Hall', happierThanAvg: 46, extroverted: 32, workLifeBalance: 47},
{dorm: 'New House', happierThanAvg: 41, extroverted: 24, workLifeBalance: 48},
{dorm: 'Next House', happierThanAvg: 35, extroverted: 18, workLifeBalance: 45},
{dorm: 'Random Hall', happierThanAvg: 47, extroverted: 29, workLifeBalance: 37},
{dorm: 'Senior House', happierThanAvg: 38, extroverted: 29, workLifeBalance: 38},
{dorm: 'Simmons Hall', happierThanAvg: 41, extroverted: 22, workLifeBalance: 49},
{dorm: 'Fraternities', happierThanAvg: 54, extroverted: 40, workLifeBalance: 54},
{dorm: 'Sororities', happierThanAvg: 56, extroverted: 52, workLifeBalance: 45},
{dorm: 'ILGs', happierThanAvg: 63, extroverted: 22, workLifeBalance: 33},
{dorm: 'Off Campus', happierThanAvg: 46, extroverted: 37, workLifeBalance: 41},
{dorm: 'Ashdown House', happierThanAvg: 37, extroverted: 25, workLifeBalance: 34},
{dorm: 'Edgerton House', happierThanAvg: 40, extroverted: 21, workLifeBalance: 47},
{dorm: 'Sidney-Pacific', happierThanAvg: 42, extroverted: 31, workLifeBalance: 33},
{dorm: 'Tang Hall', happierThanAvg: 34, extroverted: 26, workLifeBalance: 33},
{dorm: 'The Warehouse', happierThanAvg: 50, extroverted: 47, workLifeBalance: 41},
{dorm: 'Eastgate', happierThanAvg: 56, extroverted: 30, workLifeBalance: 51},
{dorm: 'Westgate', happierThanAvg: 43, extroverted: 30, workLifeBalance: 43}
]
var width = 420
var barHeight = 20;
// utility function that maps [0 to 100] to [0, width]
// draw this on the board as a map of two lines
var scaleFunction = d3.scale.linear().domain([0, 100]).range([0, width]);
// the main function that uses d3 to both create and update the bar chart
function updateBarChart() {
var chart = d3.select("#d3chart"); // select the chart itself
// This line binds our data to a set of DOM elements, specifically:
//
// mitPressureSurvey <--> '#d3chart g'
//
// Each element in the mitPressureSurvey list corresponds to a SVG 'g'
// element inside of #d3chart.
var binding = chart.selectAll("g")
.data(mitPressureSurvey, function(d) {
// This function, passed as the second argument
// to data(), specifies that the dorm name should
// be used as the identifying key for each
// element in mitPressureSurvey. Without this
// function, the sorting won't work properly.
return d.dorm;
});
// enter() specifies what happens when you create the bar chart FOR
// THE FIRST TIME. first append a SVG 'g' element for each element in
// mitPressureSurvey ...
var enterG = binding.enter()
.append("g")
// ... then append a 'rect' (rectangle) inside of each 'g', with a
// width proportional to happierThanAvg, to represent each bar.
enterG.append("rect")
.attr("width", function(d) {
return scaleFunction(d.happierThanAvg);
})
.attr("height", barHeight - 1);
// ... then append a 'text' node inside of each 'g' (after 'rect')
// with the dorm name as the label.
enterG.append("text")
.attr("x", 3)
.attr("y", barHeight / 2 + 3)
.text(function(d) {return d.dorm;});
// OK here's the really subtle part: If we call methods directly on
// the binding variable (without first calling .enter()), it specifies
// what happens EVERY TIME this updateBarChart() runs, not just the
// first time. Remember, all the code above in the enter() section ran
// only once when the chart was first created. The code below runs
// every time you call updateBarChart().
//
// So what does this code do? It first initiates a smooth transition
// with a duration of 1500ms (1.5 seconds). This transition sets the
// "transform" attribute of each 'g' element to the appropriate
// coordinates, based on the index (i) of the corresponding element in
// mitPressureSurvey.
//
// Thus, when you sort the list, the index (i) of each element
// corresponds to its sort order, and d3 knows to move it into the
// proper coordinates.
//
// In d3 terminology, this is called an 'update' selection. See:
// - http://bost.ocks.org/mike/join/
// - http://bl.ocks.org/mbostock/3808218
binding.transition()
.duration(1500)
.attr("transform", function(d, i) {
return "translate(0," + i * barHeight + ")";
});
}
// sort mitPressureSurvey by happierThanAvg and update the chart
function sortAndUpdate(isAscending) {
mitPressureSurvey.sort(function(a, b) {
if (isAscending) {
return d3.ascending(a.happierThanAvg, b.happierThanAvg);
}
else {
return d3.descending(a.happierThanAvg, b.happierThanAvg);
}
});
// update the EXISTING bar chart with the newly-sorted mitPressureSurvey
updateBarChart();
}
// run this code after your page loads
$(function() {
// set the initial dimensions for the SVG container
// (very important or else the entire chart won't display)
d3.select("#d3chart")
.attr("width", width)
.attr("height", barHeight * mitPressureSurvey.length);
// create the bar chart for the first time
updateBarChart();
// set button click handlers
$("#sortAscButton").click(function() {sortAndUpdate(true);});
$("#sortDescButton").click(function() {sortAndUpdate(false);});
});
</script>
</head>
<body>
<h1>MIT pressure survey visualization</h1>
<div style="margin-bottom: 10px;">
<button id="sortAscButton">Sort ascending</button>
<button id="sortDescButton">Sort descending</button>
</div>
<div>
<svg class="chart" id="d3chart"></svg>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment