Published
Edited
May 8, 2019
Insert cell
md`# S.js UniqueIndex`
Insert cell
S = require('s-js')
Insert cell
SArray = require('s-array')
Insert cell
map = SArray.map
Insert cell
UNSET = {};
Insert cell
function uniqueIndex(array, keyFn) {
const
index = {},
// how many items have this key. must be 0 or 1 at end of update
slotCounts = {},
// change data: which keys, items and how many are changing
changeKeys = [],
changeItems = [], // parallel to changeKeys
slot = (id) => {
return id in index ? index[id] : index[id] = S.data(null);
};
let changeCount = 0;

const
incKey = (item, key) => {
if (key in slotCounts) slotCounts[key]++
else slotCounts[key] = 1;
changeKeys[changeCount] = key;
changeItems[changeCount] = item;
changeCount++;
},
decKey = (item, key) => {
slotCounts[key]--;
changeKeys[changeCount] = key;
changeItems[changeCount] = UNSET;
changeCount++;
},
changeBuilder = map(array,
(item, oldkey) => {
const key = keyFn(item);
if (key !== oldkey) {
if (oldkey !== undefined) decKey(item, oldkey);
incKey(item, key);
}
return key;
},
decKey
),
updater = S.on(changeBuilder, () => {
let errors = "";

while (changeCount) {
changeCount--;
const
key = changeKeys[changeCount],
item = changeItems[changeCount],
slotCount = slotCounts[key],
_slot = slot(key);
changeItems[changeCount] = UNSET; // memory safe: remove ref to item

if (slotCount === 0) {
_slot(null);
} else if (slotCount === 1) {
if (item !== UNSET) _slot(item);
} else {
if (item !== UNSET) errors += "key: " + key + ", item: " + item + ". ";
slotCounts[key] = 1; // restore sane state
}
}
if (errors) throw new Error("UniqueIndex collision(s): " + errors);
});

return (id) =>
typeof id === 'function' ? () => slot(id())() : slot(id);
}

Insert cell
ix = uniqueIndex(SArray.default(["a", "b"]), _ => _)
Insert cell
ix("b")()
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