Public
Edited
Sep 17, 2024
Importers
5 stars
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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more