2022-10-24 09:22:45 +02:00

91 lines
2.4 KiB
JavaScript

'use strict';
const _ = require('lodash/fp');
// TODO: Document this class properly
/* Constraints:
- Expects you will never connect a relation before / after one that does not exist
- Expect the last initArr element to be the last relation in the database
*/
class FractionalOrderer {
constructor(initArr, idColumn, orderColumn) {
this.arr = _.castArray(initArr || []).map((r) => ({
init: true,
id: r[idColumn],
order: r[orderColumn],
}));
}
_updateRelationOrder(r) {
let idx;
// TODO: Throw if the relation does not exist
if (r.before) {
const { idx: _idx, relation } = this.findRelation(r.before);
if (relation.init) r.order = relation.order - 0.5;
else r.order = relation.order;
idx = _idx;
} else if (r.after) {
const { idx: _idx, relation } = this.findRelation(r.after);
if (relation.init) r.order = relation.order + 0.5;
else r.order = relation.order;
idx = _idx + 1;
} else if (r.start) {
r.order = 0.5;
idx = 0;
} else {
const lastRelation = this.arr.at(-1);
// TODO: Use a big number instead of lastRelation.order
if (lastRelation.init) r.order = lastRelation.order + 0.5;
else r.order = lastRelation.order;
idx = this.arr.length;
}
return { relation: r, idx };
}
// TODO: Improve performance by using a map
findRelation(id) {
const idx = this.arr.findIndex((r) => r.id === id);
return { idx, relation: this.arr[idx] };
}
connect(relations) {
_.castArray(relations).forEach((relation) => {
this.disconnect(relation);
const { relation: _relation, idx } = this._updateRelationOrder(relation);
// Add to the chunk
this.arr.splice(idx, 0, _relation);
});
return this;
}
disconnect(relations) {
_.castArray(relations).forEach((relation) => {
const { idx } = this.findRelation(relation.id);
if (idx >= 0) {
this.arr.splice(idx, 1);
}
});
return this;
}
/**
* Get a map between the relation id and its order
*/
getOrderMap() {
return _(this.arr)
.groupBy('order')
.reduce((acc, relations) => {
if (relations.at(0).init) return acc;
relations.forEach((relation, idx) => {
acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
});
return acc;
}, {});
}
}
module.exports = FractionalOrderer;