Skip to content

Instantly share code, notes, and snippets.

@Andrew-Reid
Created March 15, 2021 03:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Andrew-Reid/6fdfe3d2415ddd16751300965835da84 to your computer and use it in GitHub Desktop.
Save Andrew-Reid/6fdfe3d2415ddd16751300965835da84 to your computer and use it in GitHub Desktop.
Key function
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
</head>
<body>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", 960)
.attr("height", 1200)
.append("g")
.attr("transform","translate(10,10)");
var g = svg.append("g")
.attr("transform","translate(0,80)");
var dataset = [
{id: "A"}, {id:"B"}, {id:"C"}
]
//
g.append("rect")
.attr("x", 255)
.attr("width", 90)
.attr("height", 500)
.attr("stroke-width", 1)
.attr("stroke", "#777")
.attr("fill","none")
.attr("stroke-dasharray","2 2");
g.append("text")
.text("enter")
.attr("x", 300)
.attr("y", 20)
.attr("text-anchor","middle");
g.append("text")
.text("(create new)")
.attr("x", 300)
.attr("y", 40)
.attr("text-anchor","middle");
g.append("rect")
.attr("x", 355)
.attr("width", 90)
.attr("height", 500)
.attr("stroke-width", 1)
.attr("stroke", "steelblue")
.attr("fill","none")
.attr("stroke-dasharray","2 2");
g.append("text")
.text("update")
.attr("x", 400)
.attr("y", 20)
.attr("text-anchor","middle");
g.append("text")
.text("(existing)")
.attr("x", 400)
.attr("y", 40)
.attr("text-anchor","middle");
g.append("rect")
.attr("x", 455)
.attr("width", 90)
.attr("height", 500)
.attr("stroke-width", 1)
.attr("stroke", "crimson")
.attr("fill","none")
.attr("stroke-dasharray","2 2");
g.append("text")
.text("exit")
.attr("x", 500)
.attr("y", 20)
.attr("text-anchor","middle");
g.append("text")
.text("(excess)")
.attr("x", 500)
.attr("y", 40)
.attr("text-anchor","middle");
g.append("rect")
.attr("x", 605)
.attr("width", 90)
.attr("height", 500)
.attr("stroke-width", 1)
.attr("stroke", "steelblue")
.attr("fill","none")
.attr("stroke-dasharray","2 2");
g.append("text")
.text("unselected")
.attr("x", 650)
.attr("y", 20)
.attr("text-anchor","middle");
var dataLabel = g.append("text")
.text("dataset")
.attr("x", 80)
.attr("y", 40)
.attr("text-anchor","middle");
var lines = g.append("g");
var code = svg.append("text")
.attr("x", 100)
.attr("y", 40)
.attr("font-size",20)
.style("font-family","monospace");
var caption = svg.append("text")
.attr("x", 100)
.attr("y", 60)
.attr("font-size",14)
var randomData = false;
function addButton(f) {
var button = svg.append("g")
.attr("class","button")
button.append("rect")
.attr("width", 60)
.attr("height", 30)
.attr("x", 20)
.attr("y", 20)
.attr("fill","yellow")
.attr("stroke","#888")
.attr("stroke-width", 1)
button.append("text")
.attr("x", 50)
.attr("text-anchor","middle")
.attr("y", 42)
.text("Next")
.style("cursor","pointer");
button.on("click", function() {
d3.select(this).remove();
f();
});
}
function update(data) {
// Make selection:
addButton(makeSelection);
d3.selectAll(".datum").remove();
d3.selectAll(".key").remove();
d3.selectAll(".connector").remove();
d3.selectAll(".exit").remove();
g.selectAll(".element")
.attr("transform", (d,i) => {
return "translate("+[650,i*80+90]+")";
})
code.text("");
caption.text("Waiting to make a selection");
function makeSelection() {
code.text("var selection = svg.selectAll('selector')");
var elements = g.selectAll(".element")
caption.text("Select existing elements matching selector. Selected " + elements.size() + " matching and existing elements");
elements.attr("transform", (d,i) => {
return "translate("+[400,i*80+90]+")";
})
addButton(assignDataToSelection);
}
function assignDataToSelection() {
code.html("<tspan font-weight='bold'>selection.data(data</tspan>,d=>d.id)");
caption.text("Bind data to selection");
var datums = g.selectAll(null)
.data(data)
.enter()
.append("g")
.attr("class","datum")
.attr("transform",function(d,i) {
return "translate("+[80,i*80+90]+")";
})
datums.append("circle")
.attr("fill", "none")
.attr("stroke","#ccc")
.attr("stroke-width", 3)
.attr("r", 36)
datums.append("text")
.attr("dy", -6)
.attr("text-anchor", "middle")
.text(d=>"{id:"+d.id+"}")
.attr("font-family","monospace")
addButton(computeElementKeys);
}
function computeElementKeys() {
code.html("selection.data(data,<tspan font-weight='bold'>d=>d.id</tspan>)");
caption.text("Find keys for each element in selection, if any elements exist");
var elements = d3.selectAll(".element")
elements.append("text")
.attr("class","key")
.attr("text-anchor","middle")
.attr("dy",12)
.attr("font-family","monospace")
.text((d,i)=>"🔑:"+key(d,i))
.attr("opacity",0)
.transition()
.duration(1000)
.delay((d,i)=>i*200)
.attr("opacity",1);
addButton(computeDataKeys);
}
function computeDataKeys() {
code.html("selection.data(data,<tspan font-weight='bold'>d=>d.id</tspan>)");
caption.text("Find keys for each item in data array");
var datums = g.selectAll(".datum")
datums.append("text")
.attr("text-anchor","middle")
.attr("dy", 12)
.attr("font-family","monospace")
.text((d,i)=>"🔑:"+key(d,i))
.attr("opacity",0)
.attr("class","key")
.transition()
.duration(1000)
.delay((d,i)=>i*200)
.attr("opacity",1);
addButton(matchKeys);
}
function matchKeys() {
caption.text("Match Keys. If datum is unpaired, add new element, bind the datum to it.");
var elements = d3.selectAll(".element");
g.selectAll(".datum").each(function(d,i) {
var match = elements.filter(function(e,j) {
return key(d,i) == key(e,j);
})
var n = d3.selectAll(".element").size();
if(!match.size()) {
var el = g.append("g")
.attr("transform","translate("+[300,n*80+90]+")")
.attr("class","element enter")
.datum(d);
el.append("circle")
.attr("fill", "none")
.attr("stroke","#ccc")
.attr("stroke-width", 3)
.attr("r",36)
el.append("text")
.attr("dy", -6)
.attr("text-anchor","middle")
.text("{id:"+d.id+"}")
.attr("font-family","monospace")
// Add a path:
var x1 = 300;
var y1 = n*80+90;
var x0 = 80;
var y0 = i*80+90;
lines.append("path")
.attr("d", pather(x0+40,y0,x1-40,y1))
.attr("fill","none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("class","connector")
}
else {
// get index:
var k;
elements.filter(function(e,j) {
if(e == match.datum()) k = j;
return key(d,i) == key(e,j);
});
match.attr("transform","translate("+[400,k*80+90]+")").datum(d);
match.select("text")
.attr("dy", -6)
.attr("text-anchor","middle")
.text("{id:"+d.id+"}")
.attr("font-family","monospace")
// Add a path:
var x1 = 400;
var y1 = k*80+90;
var x0 = 80;
var y0 = i*80+90;
lines.append("path")
.attr("d", pather(x0+40,y0,x1-40,y1))
.attr("fill","none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("class","connector")
}
addButton(exit);
})
function exit() {
caption.text("Add unpaired elements to exit selection");
d3.selectAll(".element")
.data(data,key)
.exit()
.attr("opacity",1)
.classed("exit",true)
.transition()
.attr("transform", function(d,i) {
return "translate("+[500,i*80+90]+")";
})
.attr("opacity", 0.5)
addButton(startOver)
}
function startOver() {
caption.text("start again");
addButton(function() {
if(!randomData) {
randomData = true;
update([{id:"D"},{id: "B"}, {id:"A"}]);
}
else {
var string = "ABCDEF".split("");
var arr = [];
while(arr.length < 3) {
var r = Math.floor(Math.random() * 6);
if(arr.indexOf(r) === -1) arr.push(r);
}
update(arr.map(d=>{return {id:string[d]} }));
}
})
}
}
}
update(dataset);
function pather(a,b,c,d) {
var s = " ";
var m = "M";
var l = "L";
return m+a+s+b+l+(a+50)+s+b+l+(c-50)+s+d+l+c+s+d;
}
function key(d,i) {
return d.id;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment