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

4
  • 1
    what prevents you from changing the object returned by the function? This seems like an XY Problem Commented Feb 5, 2014 at 4: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. Commented Feb 5, 2014 at 5:34
  • Possible duplicate of Conditionally set a JSON object property Commented Mar 21, 2016 at 1:20
  • Possible duplicate of stackoverflow.com/questions/11704267/… Commented Mar 7, 2017 at 16:26

8 Answers 8

60

If you can use ES6, use the spread properties.

function create(includeB) {
    return {
        a : 1,
        ...(includeB ? { b: 2 } : {}),
    };
}
Sign up to request clarification or add additional context in comments.

2 Comments

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.
you can even write ...(includeB && { b: 2 } )
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
    };
}

2 Comments

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!
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 }; }
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;
}

4 Comments

Thanks. Yes, it must be done within the literal. I thought I was probably asking a lot!
@Murrah - I added two more option that have you wrap the create function and add your logic outside the existing create() function.
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 - 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?
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
}

Comments

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;
}

5 Comments

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 You wouldn't be able to do it within an object. Perhaps you could try option #1 in my answer?
@Murrah: An object literal has a very specific syntax. This syntax doesn't provide any means to conditionally set a property.
You could set b to be a function which returns 2 or undefined @Murrah
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.
0

Below should work. I hope this help.

function create(includeB){

var object = {
    a: 1
};

if (includeB)
    object.b = 2;

return object;

}

1 Comment

Thanks. Same as above, cant return an object, must be a literal.
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 }

3 Comments

I think you're looking for ternary notation: condition ? true : false;
I'm not sure what this notation is called, but it seems to do what the OP asked for.
I guess it's another ternary. I expected it to return boolean or an object.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.