Public
Edited
Dec 17, 2022
Importers
Insert cell
Insert cell
Insert cell
Insert cell
data = Array.from(
mergeSortGenerators(
[
datagripAnnualPlan(config.start),
datagripMonthlyPlan(config.start),
beekeeperBusinessPlan(config.start)
],
(d) => (d.timestamp <= config.end ? d.timestamp : null)
)
)
Insert cell
// simple $22.90/month plan
datagripMonthlyPlan = (start) =>
accumulate(
objGen({
cost: repeat(22.9),
timestamp: stepWith(start, (d) => d3.timeMonth.offset(d, 1)),
plan: repeat("datagrip-monthly")
}),
(d) => d.cost
)
Insert cell
// $199/year plan
beekeeperBusinessPlan = (start) =>
accumulate(
objGen({
cost: repeat(199),
timestamp: stepWith(start, (d) => d3.timeYear.offset(d, 1)),
plan: repeat("beekeeper-business-annual")
}),
(d) => d.cost
)
Insert cell
// costs 229 the first year, 183 the second, and 137 thereafter
datagripAnnualPlan = (start) =>
accumulate(
objGen({
cost: (function* () {
yield 229;
yield 183;
yield* repeat(137);
})(),
timestamp: stepWith(start, (d) => d3.timeYear.offset(d, 1)),
plan: repeat("datagrip-annual")
}),
(d) => d.cost
)
Insert cell
Insert cell
function* take(gen, budget) {
for (let v of gen) {
if (budget <= 0) break;
yield v;
budget--;
}
}
Insert cell
function* takeWhile(gen, cond) {
for (const v of gen) {
if (cond(v)) {
yield v;
} else break;
}
}
Insert cell
function* skipWhile(gen, cond) {
let doneSkipping = false;
for (let v of gen) {
if (doneSkipping) yield v;
else if (!cond(v)) {
doneSkipping = true;
yield v;
}
}
}
Insert cell
function objGen(obj) {
let keys = Object.keys(obj);
let gens = Array.from(Object.values(obj));
return mapGen(zip(...gens), (values) =>
Object.fromEntries(values.map((v, i) => [keys[i], v]))
);
}
Insert cell
function* mergeSortGenerators(gens, accessor) {
let nexts = gens.map((g) => g.next());
let budget = 1000;
while (nexts.some((d) => !d.done)) {
let idx = d3.minIndex(nexts, (d) => (d.done ? null : accessor(d.value)));
if (idx < 0 || nexts[idx].done) break;
yield nexts[idx].value;
nexts[idx] = gens[idx].next();
}
}
Insert cell
function* accumulate(generator, accessor, totalField = "total") {
let total = 0;
for (const d of generator) {
total += accessor(d);
yield { ...d, [totalField]: total };
}
}
Insert cell
function* stepWith(start, step) {
let v = start;
while (true) {
yield v;
v = step(v);
}
}
Insert cell
function* mapGen(gen, f) {
for (const d of gen) yield f(d);
}
Insert cell
function* zip(...gens) {
let nexts = gens.map((g) => g.next());
while (nexts.some((d) => !d.done)) {
yield nexts.map((d) => (d.done ? null : d.value));
nexts = gens.map((g) => g.next());
}
}
Insert cell
function* repeat(v) {
while (true) yield v;
}
Insert cell
function* fuse(gen) {
let last = null;
for (let v of gen) {
last = v;
yield v;
}
while (true) yield last;
}
Insert cell
function* cycle(iter) {
const past = [];
for (const v of iter) {
past.push(v);
yield v;
}
while (true) {
for (const v of past) {
yield v;
}
}
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more