Public
Edited
Feb 3, 2024
Insert cell
Insert cell
lol = [{ a: 1, b: "b" }, { a: 2, b: "" }, { b: "y" }, { a: 6 }]
Insert cell
"map" in Array.prototype
Insert cell
function proxyCol(arr, prop) {
return new Proxy(arr, {
get(obj, key) {
const val = obj[key];
return key in Array.prototype ? val : val?.[prop];
}
});
}
Insert cell
a = proxyCol(lol, "a")
Insert cell
a[2]
Insert cell
b = proxyCol(lol, "b")
Insert cell
Reflect.getPrototypeOf([])
Insert cell
({
x: (function* () {
yield 1;
yield 2;
})()
})
Insert cell
[1, 2].values()
Insert cell
function from(columns, objects) {
// Support arguments in wrong order or just single argument of either type
if (Array.isArray(columns)) [columns, objects] = [objects, columns];
const atleastOneArgumentGiven = !!columns || !!objects;
const columnsIsRightType =
!columns || (columns instanceof Object && !Array.isArray(columns));
const objectsIsRightType = !objects || Array.isArray(objects);
if (!(atleastOneArgumentGiven && columnsIsRightType && objectsIsRightType))
throw new TypeError(
"Bad arguments, should be one array of objects or one object of columns or both in any order"
);

// Trust the length of the objects array or the first column
const length = (objects ?? Object.values(columns).at(0))?.length;
const marker = Symbol();

return {
length,
*[Symbol.iterator]() {
let i = 0;
const proxy = new Proxy(
{},
{
get(_, prop) {
if (prop === marker) return true;
return columns && prop in columns
? columns[prop]?.[i]
: objects?.[i]?.[prop];
},
ownKeys(_) {
return (columns ? Object.getOwnPropertyNames(columns) : []).concat(
objects && objects[i]
? Object.getOwnPropertyNames(objects[i])
: []
);
},
has(_, prop) {
return this.ownKeys().includes(prop);
},
getOwnPropertyDescriptor(_, prop) {
return {
enumerable: true,
configurable: true,
value: this.get(0, prop)
};
}
}
);
for (; i < length; ++i) yield proxy;
},
where(predicate) {
const prev = this;
return {
...this,
*[Symbol.iterator]() {
for (const it of prev) if (predicate(it)) yield it;
}
};
},
select(selector) {
const prev = this;
return {
...this,
*[Symbol.iterator]() {
for (const it of prev) yield selector(it);
}
};
},
array(type = Array) {
return type.from(this, (it) => (it[marker] ? { ...it } : it));
},
map(f) {
return this.select(f);
},
filter(p) {
return this.where(p);
}
};
}
Insert cell
function from_(columns, objects) {
// Support arguments in wrong order or just single argument of either type
if (Array.isArray(columns)) [columns, objects] = [objects, columns];
const atleastOneArgumentGiven = !!columns || !!objects;
const columnsIsRightType =
!columns || (columns instanceof Object && !Array.isArray(columns));
const objectsIsRightType = !objects || Array.isArray(objects);
if (!(atleastOneArgumentGiven && columnsIsRightType && objectsIsRightType))
throw new TypeError(
"Bad arguments, should be one array of objects or one object of columns or both in any order"
);

// Trust the length of the objects array or the first column
const length = (objects ?? Object.values(columns).at(0))?.length;

// Use Lodash to enhance generator
return _(function* () {
let i = 0;
const proxy = new Proxy(
{},
{
get(_, prop) {
return columns && prop in columns
? columns[prop]?.[i]
: objects?.[i]?.[prop];
}
}
);
for (; i < length; ++i) yield proxy;
});
}
Insert cell
tests = [
from({ a: [1], b: [2], i: [1] }),
from([{ a: 1, b: 2, i: 2 }]),
from({ a: [1] }, [{ b: 2, i: 3 }]),
from([{ a: 1 }], { b: [2], i: [4] })
]
Insert cell
tests.map((it) => it.select((x) => [x.i, x.a + x.b]).array()[0])
Insert cell
function assertThrows(...args) {
try {
return from(...args);
} catch {
return true;
}
}
Insert cell
[
assertThrows([{ 1: 1 }], [{ 1: 1 }]),
assertThrows({ 1: [1] }, { 1: [1] }),
assertThrows()
]
Insert cell
c = from({ a, b })
.map((it) => `${it.a}, ${it.b}`)
.array()
Insert cell
c1 = from({ a, b })
.where((it) => it.a % 2 === 0)
.map((it) => `${it.a}, ${it.b}`)
.array()
Insert cell
from({ a, b }).array()
Insert cell
{
const it = from({ a, b }).array();
return it;
}
Insert cell
from({ a, b })
.select((it) => ({ ...it }))
.array()
Insert cell
## Apache Arrow comparison
Insert cell
Insert cell
arrow = require("apache-arrow@15.0.0/Arrow.es2015.min.js")
Insert cell
t = arrow.tableFromJSON(lol)
Insert cell
t.toArray()
Insert cell
t.getChild("a")
Insert cell
[t.get(2).a, t.get(3).b]
Insert cell
t.batches
Insert cell
Insert cell
arr_a = arrow.vectorFromArray(lol.map(({ a }) => a))
Insert cell
arr_a.get(2)
Insert cell
arr_a_nulls = arrow.vectorFromArray(lol.map(({ a = null }) => a))
Insert cell
arr_a_nulls.get(2)
Insert cell
arr_b = arrow.vectorFromArray(lol.map(({ b }) => b))
Insert cell
arr_b.get(3)
Insert cell
arr_b_nulls = arrow.vectorFromArray(lol.map(({ b = null }) => b))
Insert cell
arr_b_nulls.get(3)
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