Javascript pure function example with objects

I have the example below of an impure function. Note the variable a is outside of the function scope is being changed. To get around it, one could clone the object inside the function and return a copy, but is that the right way?

What are some options to make the function transformObject pure?

var a = {
    a : 1,
    b : 2
};

function transformObject(obj) {
    var ref = obj;
    _.each(ref, function(val, index){
        ref[index] = val*2;
    });
    return ref;
}

s=JSON.stringify
$('#code').text(s(transformObject(a)))
$('#code2').text(s(a))

https://jsfiddle.net/br17kk2h/1/

Answers:

Answer

A pure function follows three main principles:

The function has a single responsibility

The function should do only one thing.

The function has no side effects

The function does not change state outside its scope.

The function is referentially transparent

The function outputs the same value for the same inputs.

The next step is to explore the impact of these principles in the design of the function.

Your function already meets the first principle, although the name should be improved ;)

And it meets the second principle too, because it doesn't modify anything else outside its scope (its input parameters).

Unfortunately it doesn't meet the third principle:

var a = {
  a : 1,
  b : 2
};

var b = transformObject(a);//b = {2, 4}

var c = transformObject(a);//c = {4, 8}

Same inputs (a) but different outputs. That violates the third principle.

Referentially transparent functions needs immutable data.

A good answer has already been posted by Nina Scholz:

function transformObject(a) {
  var b = {};
  Object.keys(a).forEach(function (k) { b[k] = a[k] * 2; });
  return b;
}

But it only works because the input object does not contain any other object nested in it.

Right now there are some good libraries that gives you immutable structures (like ImmutableJS).

A simple example using ImmutableJS:

describe('a List', () => {

    function addMovie(currentState, movie) {
        return currentState.push(movie);
    }

    it('is immutable', () => {

        let state = List.of('Avengers', 'Antman');
        let nextState = addMovie(state, 'Superman');

        expect(nextState).to.equal(List.of(
            'Avengers',
            'Antman',
            'Superman'
        ));

        expect(state).to.equal(List.of(
            'Avengers',
            'Antman'
        ));
    });

    it('can be updated and returns another immutable List', () => {

        let state = List.of(1,2,3,4);

        let nextState = state.map(value => value*2);

        expect(nextState).to.equal(List.of(2,4,6,8));
        expect(state).to.equal(List.of(1,2,3,4));
    });
});

You can read more about immutable API here

Answer

Change the object:

function transformObject(a) {			
    Object.keys(a).forEach(function (k) { a[k] *= 2; });
}

var a = {
        a : 1,
        b : 2
    };
transformObject(a);		
document.write('<pre>' + JSON.stringify(a, 0, 4) + '</pre>');

Keep the object and return a new object with the result:

function transformObject(a) {
    var b = {};
    Object.keys(a).forEach(function (k) { b[k] = a[k] * 2; });
    return b;
}

var a = {
        a : 1,
        b : 2
    },
    b = transformObject(a);		
document.write('<pre>' + JSON.stringify(a, 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(b, 0, 4) + '</pre>');

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.