Public
Edited
Sep 17, 2024
Importers
5 stars
Color
Boilerplate
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Ensures the object is an iterable list

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md`TODO: Optimise`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
isIterator = (_) =>
false &&
_ &&
(typeof _.next === "function" || typeof _[Symbol.iterator] === "function")
Insert cell
isCollection = (value) => _ !== null && typeof value === "object"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
state = ( ()=>{
const state = {};
return (name,value=undefined) => {
if (value === undefined) {
return state[name];
} else {
state[name] = value;
return value;
}
}
})()
Insert cell
Insert cell
len = v => maptype(v,{
list: _ => _.length,
object: _ => typeof _.length === "number" ? _.length : Object.keys(v).length,
string: _ => _.length,
_:1,
null: _ => 0,
undefined: _ => 0})
Insert cell
values = v => maptype(v,{
list: idem,
object: _ => Object.values(v),
_:[v],
null: _ => [],
undefined: _ => []})
Insert cell
keys = v => maptype(v,{
list: _ => _.map( (_,i) => i ),
object: _ => Object.keys(v),
_:[v],
null: _ => [],
undefined: _ => []})
Insert cell
items = (collection, asList=true) => {
let isList = false;
// TODO: Should use iter
switch (type(collection)) {
case "list":
isList = true;
case "object":
const res = [];
let i=0;
if (asList) {
for (let k in collection) {
res[i++] = [isList ? i : k, collection[k]];
}
} else {
for (let k in collection) {
res[i++] = {key:k, value:collection[k]};
}
}
return res;
default:
return []
}
}
Insert cell
Like `items(v)` but returns `{key,value}` instead of `[key,value]`
Insert cell
keyvalues = _ => items(_, false)
Insert cell
first([1, 2, 3])
Insert cell
first({a:1, b:2, c:3})
Insert cell
def(undefined, 10)
Insert cell
first(
{ a: 1, b: 2, c: 3 },
(v, k) => v >= 200,
(v, i) => i
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
last = value => nth(value, -1)
Insert cell
Insert cell
Insert cell
isIn = (collection, value) => index(collection, value) !== -1
Insert cell
Insert cell
Insert cell
difference = (a,b) => filter(a, _ => index(b,_) == -1)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
slice([10, 20, 30, 40], 2, 4)
Insert cell
slice({ a: 10, b: 20, c: 30, d: 40 }, 2, 4)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
array = (count,creator=null) => {
const res = new Array(count);
while(count) {count--;res[count] = creator ? creator(count) : count}
return res;
}
Insert cell
Insert cell
mapkeys = (collection, functor) =>
reduce(
collection,
(r, v, k) => {
r[functor(k, v)] = v;
},
empty(collection)
)
Insert cell
maplist = (collection,functor) =>
reduce(collection, (r,v,k,l)=>{r.push(functor(v,k,l));}, [])
Insert cell
reduce = (collection,processor,initial=undefined) =>
iter(collection,
(v,k,r) => {const w=processor(r,v,k,collection);return w === undefined ? r : w},
idem,
initial === undefined ? empty(collection) : initial,
initial)
Insert cell
filter = (collection, predicate=bool, processor=idem) =>
iter(collection,
type(collection) === "list"
? (v,i,r) => {predicate(v,i,collection) !== false && r.push(processor(v,i));return r}
: (v,k,r) => {if (predicate(v,k,collection) !== false) {r[k]=processor(v,k)};return r},
idem,
empty(collection),
collection)
Insert cell
each = (collection,functor) => iter(collection,functor,undefined,collection)
Insert cell
merge = (value, other, replace = false) => {
if (value === null || value === undefined) {
return other;
} else if (other === null || value === other) {
return value;
} else {
for (let k in other) {
const v = value[k];
const w = other[k];
if (v === undefined) {
value[k] = w;
} else if (replace === true) {
value[k] = w;
} else if (replace instanceof Function) {
value[k] = replace(value[k], w);
}
}
return value;
}
}
Insert cell
rmerge = (() => {
const rmerge = (value, other, depth = -1) => {
if (depth === 0) {
return value;
} else if (value === null || value === undefined) {
return other;
} else if (other === null || value === other) {
return value;
} else if (other instanceof Object && value instanceof Object) {
return reduce(
other,
(r, v, k) => {
r[k] = rmerge(r[k], v, depth > 0 ? depth - 1 : depth);
},
value
);
} else {
return other;
}
};
return rmerge;
})()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
`enumerate("A", "B", ..)` will return `{A:"A",B:"B"B,...}` as a frozen object.
Insert cell
enumerate = (...values) =>
freeze(
reduce(
values,
(r, v) => {
r[v] = v;
return r;
},
{}
)
)
Insert cell
Insert cell
Insert cell
Insert cell
stripe = (values) => {
const n = len(values);
const m = Math.floor(n/2);
return array(n, i =>
values[i < m
? Math.min(n-1,i * 2)
: Math.min(n-1,1 + (i-m)*2)]
)
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sinestep = x => (Math.cos(Math.PI+Math.PI*x) + 1) / 2
Insert cell
smoothstep = x => x * x * x * (x * (x * 6 - 15) + 10)
Insert cell
smootherstep = x => x*x*x*(x*(x*6-15)+10)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nicer = (value, affinity = 0, values = [1, 5, 10, 20, 25, 50, 100]) => {
const v = Math.abs(value)
const s = sign(value)
const k = Math.pow(10, Math.floor(order(v)) -1)
return s * closestInOrder(values, v/k, affinity*s) * k
}
Insert cell
Insert cell
subdivide = (start=0,end=1,steps=100,closed=true) =>
// TODO: Support nice start and end?
steps <= 0
? []
: start===end
? (closed ? [start] : [])
: range(start,end,(end-start)/(steps - (closed ? 1 : 0)),closed)

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sum = collection => reduce(collection, add, 0)
Insert cell
extent = series => Math.abs(sub(minmax(series)))
Insert cell
midpoint = series => lerp(...minmax(series), 0.5)
Insert cell
gradients = series =>
reduce(list(series), (r,v,i,l) => {i > 0 && r.push(v - l[i -1])})
Insert cell
mean = series =>
sum(series)/len(series)
Insert cell
Insert cell
rmsd = (series, target=1) =>
Math.sqrt(reduce(series, (r,v) => r + sqdist(v, target), 0) / len(series))
Insert cell
Insert cell
reducematches = (text, regexp, reducer, initial = []) => {
let res = initial;
let i = 0;
let o = 0;
let r = undefined;
for (const match of text.matchAll(regexp)) {
if (o != match.index) {
r = reducer(res, text.substring(o, match.index), i++);
}
res = r === undefined ? res : r;
r = reducer(res, match, i++);
o = match.index + match[0].length;
res = r === undefined ? res : r;
}
if (o != text.length) {
r = reducer(res, text.substring(o), i++);
}
res = r === undefined ? res : r;
return res;
}
Insert cell
toKebabCase = (words) => reduce(words, (r,v) => {r.push(v.toLowerCase())}, []).join("-")

Insert cell
fromKebabCase = (text) => text.split("-").map((_) => _.strip())
Insert cell
fromPascalCase("CommandOutput")
Insert cell
fromPascalCase = (text) =>
reducematches(text, /[A-Z][a-z]+/g, (r, v) => {
r.push(typeof v === "string" ? v : v[0]);
})
Insert cell
capitalize("hello")
Insert cell
capitalize = (text) => `${text.substr(0, 1).toUpperCase()}${text.substr(1)}`
Insert cell
Insert cell
asNumber = (value) => {
if (value === undefined || value === null) {
return 0;
}
if (typeof value === "number") {
return isNaN(value) ? 0 : value;
}
return value instanceof Date ? value.getTime() : 0;
}
Insert cell
Insert cell
delta = (series,distance=sub) => series.reduce((r,v,i)=>{
if (i > 0) {r.push(distance(v, series[i - 1]))}
return r;
}, [])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// TODO: Support giving a component to style it
stylesheet = (rules, name = "default", waitForAvailability = true) => {
const id = `stylesheet-${name || "default"}`;
const existing = document.getElementById(id);
const node = existing || html.style({ id, name });
const updater = () => {
const sheet = node.sheet;
if (!sheet) {
return false;
}
while (sheet && sheet.cssRules && sheet.cssRules.length) {
sheet.deleteRule(0);
}
Object.entries(rules).forEach(([k, v]) => {
// We support {@:[...]} for imports and such
if (k === "@") {
each(v, (_) => sheet.insertRule(`${v};`, 0));
} else {
const w = maptype(v, {
object: (_) =>
Object.entries(_)
.map(([k, v]) => `${k}: ${v};`)
.join("\n"),
array: (_) => _.join("\n"),
_: str
});
sheet.insertRule(`${k} {${w}}`, 0);
}
}, sheet);
return true;
};
// In Observable, the node will only be mounted at a later point, and we need
// to test for the sheet and cssRules properties to be set before actually doing
// the update. In other environments, we can just proceed with the update already.
!updater() &&
waitForAvailability &&
until(
() => {
return node.sheet && node.sheet.cssRules;
},
updater,
100,
10
);
return node;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
setattr = (node, name, value, append = 0, ns = undefined) => {
const t = typeof value;
if (!ns && name.startsWith("on")) {
const n = name.toLowerCase();
if (node[n] !== undefined) {
// We have a callback
node[n] = value;
}
return node;
}
if (!ns & (name === "style") && t === "object") {
// We manage style properties by valle
if (!append) {
node.setAttribute("style", "");
}
Object.assign(node.style, value);
} else if (!ns && name === "value" && node.value !== undefined) {
node.value = value ? value : "";
} else if (!ns && name.startsWith("on") && node[name] !== undefined) {
// We have a callback
node[name] = value;
} else if (value === undefined || value === null) {
// We remove the attribute
ns ? node.removeAttributeNS(ns, name) : node.removeAttribute(name);
} else {
// We have a regular value that we stringify
const v =
t === "number"
? `${value}`
: t === "string"
? value
: JSON.stringify(value);
// If we append, we create an inermediate value.
if (append) {
const e = ns ? node.getAttributeNS(ns, name) : node.getAttribute(name);
const w = `${append < 0 && e ? e + " " : ""}${v}${
append > 0 && e ? " " + e : ""
}`;
ns ? node.setAttributeNS(ns, name, w) : node.setAttribute(name, w);
} else {
ns ? node.setAttributeNS(ns, name, v) : node.setAttribute(name, v);
}
}
return node;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sprintf("%0.2f%%", 0.12232 * 100)
Insert cell
Insert cell
Insert cell
Insert cell
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