29

I am trying to do the following to satisfy the requirements of a code builder (Sencha Cmd to be specific).

This is the essence I what I need to do. The critical factor is that the function body MUST end with a return of an object literal. I cant return a variable due to restrictions in the builder. So, how to add a property 'b' at the point of the pseudo code below if the parameter 'includeB' is true, but NOT add a property AT ALL if it is false. ie b==undefined or b==null is not allowed.

Perhaps it is not possible.

function create(includeB) {
        // Can have code here but the final thing MUST be a return of the literal.
        // ...
    return {
        a : 1
        // pseudo code:
        // if (includeB==true) then create a property called b 
        // and assign a value of 2 to it. 
        // Must be done right here within this object literal
    }
}

var obj = create(false);
// obj must have property 'a' ONLY

var obj = create(true);
// obj must have properties 'a' and 'b'

Thanks for reading and considering,

Murray

Murrah
  • 1,508
  • 1
  • 13
  • 26
  • 1
    what prevents you from changing the object returned by the function? This seems like an [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – zzzzBov Feb 05 '14 at 04:53
  • I don't have access to it. The builder is reading the JS from the file system using a Java app (Sencha Cmd). The function provided is a test case since trying to demonstrate the complete real situation would have been too confusing. The test case was the essence of the problem. – Murrah Feb 05 '14 at 05:34
  • Possible duplicate of [Conditionally set a JSON object property](http://stackoverflow.com/questions/18019854/conditionally-set-a-json-object-property) – rofrol Mar 21 '16 at 01:20
  • Possible duplicate of http://stackoverflow.com/questions/11704267/in-javascript-how-to-conditionally-add-a-member-to-an-object – djskinner Mar 07 '17 at 16:26

8 Answers8

56

If you can use ES6, use the spread properties.

function create(includeB) {
    return {
        a : 1,
        ...(includeB ? { b: 2 } : {}),
    };
}
hoshi
  • 1,677
  • 16
  • 22
  • This is elegant syntax in many cases, but requires Object Rest/Spread, which currently is currently (2017-10-29) only a Stage 3 proposal for some future ECMAScript version; work well with Babel's transform, though. – Thomas Luzat Oct 29 '17 at 00:54
  • 28
    you can even write `...(includeB && { b: 2 } )` – marco Oct 16 '18 at 14:01
5

You've pretty much shown a use case for a constructor function instead of using an object literal:

function CustomObject(includeB) {
    this.a = 1;
    if (includeB) {
        this.b = 2;
    }
}

//has `a` only
var obj1 = new CustomObject(false);

//has `a` and `b`
var obj2 = new CustomObject(true);

After re-reading your question it appears that you've got limited access in modifying the function. If I'm understanding your question correctly you can only change a limited portion of the script:

function create(includeB) {
    // modifications may be done here

    // the rest may not change
    return {
        a : 1
    }
}

var obj = create(false);
// obj must have property 'a' ONLY

var obj = create(true);
// obj must have properties 'a' and 'b'

If that's the case, then you could simply skip the later part of the function:

function create(includeB) {
    if (includeB) {
        return {
            a: 1,
            b: 2
        };
    }
    return {
        a: 1
    };
}
zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • OK. Sorry for the delay. I was testing this solution in the real app with the builder. Yes! This syntax is acceptable to the builder. I had previously tried by using an 'else' block but didn't think to try just a plain return. Thank you! – Murrah Feb 05 '14 at 05:30
  • To be clear, this is the syntax that kept the builder happy: function create(includeB) { if (includeB) { return { a: 1, b: 2 }; } return { a: 1 }; } – Murrah Feb 05 '14 at 05:37
1

You cannot put boolean logic inside a javascript literal definition. So, if your builder requires the the returned object can ONLY be defined as a javascript literal, then you cannot define properties conditionally that way.


If you can create an object inside your function, modify that object using logic and then return that object, then that's pretty easy.

function create(includeB) {
    var x = {
        a: 1
    };
    if (includeB) {
        x.b = 2;
    }
    return x;
}

Your other option would be to wrap the create function and do it outside the create function.

function myCreate(includeB) {
    var x = create(includeB)
    if (includeB) {
        x.b = 2;
    }
    return x;
}

Or, you could even wrap the create function transparently so callers still use create(), but it's behavior has been altered.

var oldCreate = create;
create = function(includeB) {
    var x = oldCreate(includeB);
    if (includeB) {
        x.b = 2;
    }
    return x;
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks. Yes, it must be done within the literal. I thought I was probably asking a lot! – Murrah Feb 05 '14 at 04:46
  • @Murrah - I added two more option that have you wrap the create function and add your logic outside the existing `create()` function. – jfriend00 Feb 05 '14 at 04:47
  • Thanks. Again, the builder requirement in the specific situation is that whatever function I give it, it must have the literal return. return x; is "illegal" in the specific case. – Murrah Feb 05 '14 at 04:51
  • @Murrah - My last two suggestions did not modify the actual `create()` function. It can be left as is without the `.b` property. I was suggesting how you could add some plain javascript that would wrap or modify the `create()` function to make it behave the way you wanted. Can you not add regular javascript to your project? – jfriend00 Feb 05 '14 at 04:52
1

I recently had to do this, and found you could use a self-calling function within an object's definition (if using ES6). This is similar to the accepted answer, but might be useful for others who need to do this without first defining a constructor function.

For example:

let obj = (() => {
  let props = { a: 1 };
  if ( 1 ) props.b = 2;
  return props;
})();

makes the object: { a: 1, b: 2 }

It's handy for more complicated objects, keeping the construction continuous:

let obj = {
  a: 1,
  b: (() => {
    let props = { b1: 1 };
    if ( 1 ) props.b2 = 2;
    return props;
    })(),
  c: 3
}

makes the object:

{
  a: 1,
  b: {
    b1: 1,
    b2: 2
  },
  c: 3
}
M P
  • 265
  • 1
  • 3
  • 11
0

You could define it later:

var hasA = create(); // has hasA.a

var hasBoth = create();
hasBoth.b = 2; //now has both

Alternatively, using your argument in create:

function create (includeB) {
  var obj = { 
    a : 1
  };
  if (includeB) {
     obj.b = 2;
  }
  return obj;
}
bozdoz
  • 12,550
  • 7
  • 67
  • 96
  • Yes, normally that is what I would do. As I say, in this case due to external requirements, I cannot do that because the code builder is essentially consuming the result of the create function, so I dont have access to it. If it is possible at all it MUST be done where the pseudo code is. – Murrah Feb 05 '14 at 04:44
  • @Murrah You wouldn't be able to do it within an object. Perhaps you could try option #1 in my answer? – bozdoz Feb 05 '14 at 04:45
  • 1
    @Murrah: An object literal has a very specific syntax. This syntax doesn't provide any means to conditionally set a property. – Felix Kling Feb 05 '14 at 04:49
  • You could set b to be a function which returns 2 or undefined @Murrah – bozdoz Feb 05 '14 at 04:49
  • Correct @zzzzBov, but it's an option. Honestly, the example given in the question doesn't seem sufficient for whatever real problem OP is trying to solve. – bozdoz Feb 05 '14 at 04:51
0

How about this:

function create(includeB) {
    return includeB && { a:1, b:2 } || { a:1 };
}

When includeB is true, the create function will return {a:1, b:2}. If includeB is false, it will return whatever is after the or - in this case, the {a:1} object.

create(true) returns { a:1, b:2 }.

create(false) returns { a:1 }

Blundering Philosopher
  • 6,245
  • 2
  • 43
  • 59
0

Below should work. I hope this help.

function create(includeB){

var object = {
    a: 1
};

if (includeB)
    object.b = 2;

return object;

}

Tee
  • 1
0

If you would like to use a declaration to satisfy the same requirement once without too much bloat, you can also simply do the following:

var created = function(includeB) {
    var returnObj = { a : 1 };

    if(includeB) { returnObj.b = 2; }

    return returnObj;
}}(); //automatically runs and assigns returnObj to created
rob2d
  • 964
  • 9
  • 16