Luke Miles
Luke Miles

Reputation: 1160

Javascript declarative/immutable version of 'maybe push'?

I have code like this:

A = [1,2,3]
if (condition) {A.push(4)}
A.push(5)

But my array is actually immutable so I can't do that. How can I do it all in one expression? I don't want a null value in the middle.

Upvotes: 0

Views: 99

Answers (2)

Aadit M Shah
Aadit M Shah

Reputation: 74204

You can use the Writer monad here.

// type Writer t a = { value :: a, array :: [t] }

// pure :: a -> Writer t a
const pure = (value) => ({ value, array: [] });

// bind :: (Writer t a, a -> Writer t b) -> Writer t b
const bind = (writerA, arrow) => {
    const writerB = arrow(writerA.value);
    const array = [...writerA.array, ...writerB.array];
    return { value: writerB.value, array };
};

// tell :: [t] -> Writer t ()
const tell = (array) => ({ value: undefined, array });

const writer = (gen) => {
    const next = (data) => {
        const { value, done } = gen.next(data);
        return done ? value : bind(value, next);
    };

    return next(undefined);
};

const example = (condition) => writer(function* () {
    yield tell([1, 2, 3]);
    if (condition) yield tell([4]);
    return tell([5]);
}());

console.log(example(true).array); // [1,2,3,4,5]
console.log(example(false).array); // [1,2,3,5]

Upvotes: 1

Luke Miles
Luke Miles

Reputation: 1160

Here is one way:

A = [1, 2, 3, ...(condition ? [4] : []), 5]

If this is common in your codebase, and you want to keep undefined then you write a filter function with a sentinel.

const REMOVE = symbol('removeme')
const A = clean([1, 2, 3, condition ? 4 : REMOVE, 5])
function clean(arr) {return arr.filter(x=>x!==REMOVE)}

Upvotes: 1

Related Questions