functional-data-structure/persistent

View on GitHub
src/seq.js

Summary

Maintainability
D
1 day
Test Coverage
export function seq(empty, size) {
    const Seq = function (tree) {
        this.tree = tree;
    };

    Seq.prototype.isEmpty = function () {
        return this.tree.isEmpty();
    };

    Seq.prototype.measure = Seq.prototype.len = function () {
        return this.tree.measure();
    };

    Seq.prototype.head = function () {
        return this.tree.head();
    };

    Seq.prototype.tail = function () {
        return new Seq(this.tree.tail());
    };

    Seq.prototype.last = function () {
        return this.tree.last();
    };

    Seq.prototype.init = function () {
        return new Seq(this.tree.init());
    };

    Seq.prototype.cons = function (value) {
        return new Seq(this.tree.cons(value));
    };

    Seq.prototype.push = function (value) {
        return new Seq(this.tree.push(value));
    };

    Seq.prototype.append = function (iterable) {
        return new Seq(this.tree.append(iterable));
    };

    Seq.prototype.prepend = function (iterable) {
        return new Seq(this.tree.prepend(iterable));
    };

    Seq.prototype.concat = function (other) {
        return new Seq(this.tree.concat(other.tree));
    };

    Seq.prototype.takeUntil = function (predicate) {
        return new Seq(this.tree.takeUntil(predicate));
    };

    Seq.prototype.dropUntil = function (predicate) {
        return new Seq(this.tree.dropUntil(predicate));
    };

    Seq.prototype.set = function (index, value) {
        if (index < 0 || index >= this.len())
            throw new Error(`wrong index '${index}'`);

        const split = this.tree.splitTree((m) => m > index, size.zero());

        return new Seq(split.left.push(value).concat(split.right));
    };

    Seq.prototype.get = function (index) {
        if (index < 0 || index >= this.len())
            throw new Error(`wrong index '${index}'`);

        return this.tree.splitTree((m) => m > index, size.zero()).middle;
    };

    Seq.prototype.insert = function (index, value) {
        if (index < 0 || index >= this.len())
            throw new Error(`wrong index '${index}'`);

        const [prefix, rest] = this.tree.split((m) => m > index);
        return new Seq(prefix.push(value).concat(rest));
    };

    Seq.prototype.remove = function (index) {
        if (index < 0 || index >= this.len())
            throw new Error(`wrong index '${index}'`);

        const {left, right} = this.tree.splitTree((m) => m > index, size.zero());
        return new Seq(left.concat(right));
    };

    Seq.prototype.slice = function (start = 0, end = this.len()) {
        if (start < -this.len() || start > this.len())
            throw new Error(`wrong start '${start}'`);
        if (end < -this.len() || end > this.len())
            throw new Error(`wrong end '${end}'`);

        if (start < 0) start += this.len();
        if (end < 0) end += this.len();

        const [, rest] = this.tree.split((m) => m > start);
        const [slice] = rest.split((m) => m > end - start);
        return new Seq(slice);
    };

    Seq.prototype.splice = function (start, deleteCount, ...items) {
        const length = this.len();

        if (start < -length || start >= length)
            throw new Error(`wrong start '${start}'`);

        if (start < 0) start += length;
        else if (start === length) {
            return [new Seq(this.tree.append(items)), new Seq(empty(size))];
        }

        const split = this.tree.splitTree((m) => m > start, size.zero());

        if (deleteCount === 1) {
            if (items.length === 0) {
                return [
                    new Seq(split.left.concat(split.right)),
                    new Seq(empty(size).push(split.middle)),
                ];
            }

            return [
                new Seq(split.left.append(items).concat(split.right)),
                new Seq(empty(size).push(split.middle)),
            ];
        }

        const prefix = split.left;
        const rest = split.right.cons(split.middle);

        if (deleteCount === 0) {
            return [new Seq(prefix.append(items).concat(rest)), new Seq(empty(size))];
        }

        if (deleteCount === undefined) {
            return [new Seq(prefix.append(items)), new Seq(rest)];
        }

        const [deleted, suffix] = rest.split((m) => m > deleteCount);
        return [new Seq(prefix.append(items).concat(suffix)), new Seq(deleted)];
    };

    Seq.prototype[Symbol.iterator] = function () {
        return this.tree[Symbol.iterator]();
    };

    Seq.prototype.split = function (predicate) {
        const [left, right] = this.tree.split(predicate);
        return [new Seq(left), new Seq(right)];
    };

    Seq.prototype.splitAt = function (index) {
        return this.split((m) => m > index);
    };

    return {
        empty: () => new Seq(empty(size)),
        from: (iterable) => new Seq(empty(size)).append(iterable),
    };
}