How to conditionally add properties to a javascript object literal - javascript

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

If you can use ES6, use the spread properties.
function create(includeB) {
return {
a : 1,
...(includeB ? { b: 2 } : {}),
};
}

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

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

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
}

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

Below should work. I hope this help.
function create(includeB){
var object = {
a: 1
};
if (includeB)
object.b = 2;
return object;
}

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 }

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

Related

JS defineProperty setter doesn't triggered

Q1: Can someone explain how to trigger setter in defineProperty, using it via function by this way?
Q2: How to get last key in setter?
fiddle is here
function test(root) {
Object.defineProperty(this, 'subtree', {
get: function() {
console.log("get");
return root.subtree;
},
set: function(value) { //doesn't triggered
console.log("set");
root.subtree = value;
}
});
}
var demo = new test({
subtree: {
state: null,
test: 1
}
});
console.log("START", demo.subtree);
demo.subtree.state = 13; // doesn't triggered setter, but change object
console.log("END", demo.subtree);
To make it simpler, this code
let variable = null;
let obj = {
set variable(value) {
variable = value;
}
get variable() {
return variable;
}
};
obj.variable = {a: 5};
console.log(obj.variable);
does exactly the same thing as this one
let variable = null;
let obj = {
setVariable(value) {
variable = value;
}
getVariable() {
return variable;
}
};
obj.setVariable({a: 5}); // equivalent to obj.variable = {a: 5}
console.log(obj.getVariable()); // equivalent to obj.variable
but the latter clearly shows what's going on.
We want to access a and set it to some value
console.log(obj.getVariable().a); // get a
obj.getVariable().a = 6; // set a!
Notice that we don't call setVariable to set a's value!!! This is exactly what happens in your code. You get subtree and set state to 13. To call setter, you do the following
obj.setVariable({a: 6});
obj.variable = {a: 6}; // getter/setter example
demo.subtree = {state: 13}; // your code
This and this (linked by you) present how scopes and capturing work, so you should get your hands on some book that covers all those things (or browse SO, there are (probably) plenty of questions about that).

Javascript prototype undefined instead of inherited?

function test() {
this.a = {
b: 4
};
}
test.prototype.a = {
c: 5
};
var example = new test();
Why is example.a.c == undefined?
Shouldn't it inherit the prototype and return 5?
If this isn't possible, is there some way to add code to return the prototype?:
function test() {
this.a = {
b: 4,
c: *this.return.prototype*
};
}
example.a either references one object or the other, you can't directly make it retrieve properties from different objects.
What I would do is making example.a an object which inherits from the other one:
function test() {
this.a = Object.create(test.a_proto);
this.a.b = 4;
}
test.a_proto = {
c: 5
};
var example = new test();
console.log(example.a.b); // 4 (own)
console.log(example.a.c); // 5 (inherited)
Define a getter for a.c that accesses the prototype.
function test() {
this.a = {
b: 4,
get c() {
return test.prototype.a.c;
}
};
}
test.prototype.a = {
c: 5
};
var example = new test();
console.log(example.a.b);
console.log(example.a.c);
// update prototype
test.prototype.a.c = 10;
console.log(example.a.c);
When you visit 'a' , it firstly find in example. If not find ,it will try to find 'a' in example construct's prototype.So it try to visit test.ptototype.c.So your code can't find examlpe.c.I think you can change your code like this.
function test() {
this.a = {
b: 4
};
}
test.prototype.c = 5;
var example = new test();
console.log(example.c);//print 5

Javascript create reference to an object property?

I understand that in javascript, primitives are passed by value and objects are passed by reference.
I'm interested in creating a workaround of some kind that would let me get a reference to an object property containing a primitive. For example, what I wish would work is:
var someObject = {a: 1, b: 2};
var myRef = someObject.b;
myRef ++;
someObject.b #=> 3
Of course, this doesn't work. I'm aware that you could create a getter and setter function instead, or use one object to reference another object, but what I'd really like is some kind of workaround that allowed me to define a variable as a reference to the property of another object, and so far it seems this just can't be done.
So, my question is simply: is this even possible, and if so, how?
Primitive types are immutable, so no, it's not possible. You can wrap your primitive type with an object, like this:
function MyNumber(n) { this.n = n; }
MyNumber.prototype.valueOf = function() { return this.n; }
var someObject = { a: 1, b: new MyNumber(2) };
var myRef = someObject.b;
MyNumber.call(myRef, myRef + 1);
console.log(+someObject.b); // convert to number with +
OR
var someObject = {
a: { value: 1 },
b: { value: 2 },
};
var myRef = someObject.b;
my_inc(myRef); // function my_inc (obj) { obj.value++; }
// someObject.b.value == 3
The React framework uses a very simple pattern to encapsulate values.
function Link(value, requestChange)
{
this.value = value;
this.requestChange = requestChange;
}
You can pass around the object, the current value can be accessed by inspecting the value property of the object, if you want to change it you can call requestChange with a new value, you can change the value. The advantage would be to have the actual "storage location" and the logic for changing the value decoupled from the value read and write access. Note that the values can also be complex objects.
You could also achieve something similar with closures:
var someObject = {
a: 1,
b: 2
};
function property(object, prop) {
return {
get value () {
return object[prop]
},
set value (val) {
object[prop] = val;
}
};
}
var ref = property(someObject, "b");
ref.value; // 2
++ref.value; // 3
someObject.b; // 3
This works because the getter and setter functions have access to whatever bindings were in scope at the time of their creation (object and prop). You can now pass ref around, store it in a data structure, etc.
No, there isn't a nice way to do it.
You can use a work-around if you want to. Something like wrapping all your primary data types with single element arrays:
var someObject = {a: [1], b: [2]};
var myRef = someObject.b;
myRef[0]++;
someObject.b[0]; // 3
That's less than ideal though, as you have to use [0] to access the property all the time. There are some cases where it can be useful though, and the default toString of a single element array is just the toString of its element, so you can use the property directly in a string context:
console.log('My value: ' + someObject.b); // 'My value: 3'
if you want to "link" or "synchronize" two properties , each of a different object, you could do it like this:
var someObject = {
a: 1,
b: 2
};
var linkedObject = {
a:1,
b:2
}
function property(object, prop) {
return {
get value () {
return object[prop]
},
set value (val) {
object[prop] = val;
}
};
}
var s_prop = 'b'
var o_ref = property(someObject, s_prop);
var tmp = linkedObject[s_prop];
Object.defineProperty(
linkedObject,
s_prop,
{
set: function(value) {
o_ref.value = value;
},
get: function() {
return o_ref.value
}
}
);
linkedObject[s_prop] = tmp
someObject.b = 333 /// linkedObject.b is also 333 now
console.log(someObject.b) // 333
console.log(linkedObject.b)// 333
linkedObject.b = {"test": 2}
console.log(someObject.b) // {test:2}
console.log(linkedObject.b)// {test:2}
someObject.b.test = 3
console.log(someObject.b) // {test:3}
console.log(linkedObject.b)//{test:3}
I don't know how satisfying this is, but you could do it if you were ok with wrapping the desired object in an object like so:
var a = {a:{a:1},b:2};
var b = a.a;
b.a++;
a.a.a //=> 2
It isn't exactly what you asked for, but it would work.

Parse object dot notation to retrieve a value of an object

I'm finding myself struggling with a little problem.
Let's say I've got an object:
var foo = {
bar: {
baz: true
}
};
Now I also have a String 'foo.bar.baz'. I'd now like to retrieve the value from the object using the string.
Please note: This is just an example, the solution needs to be dynamic.
Update:
I need the variable name also to be dynamic and parsed from the string. Also I can't be sure that my variable is a property of the window.
I have already built a solution using eval, but this is pretty ugly I think:
http://jsfiddle.net/vvzyX/
For example,
function get(obj, path) {
return path.split('.').reduce(function(obj, p) {
return obj[p]
}, obj);
}
Demo:
tree = {
foo: {
bar: 1,
baz: { quux: 3 },
},
spam: 1
}
console.log(get(tree, 'foo.baz.quux')) // 3
Here is how you can do this:
function getValue(namespace, parent) {
var parts = namespace.split('.'),
current = parent || window;
for (var i = 0; i < parts.length; i += 1) {
if (current[parts[i]]) {
current = current[parts[i]];
} else {
if (i >= parts.length - 1)
return undefined;
}
}
return current;
}
var foo = {
bar: {
baz: true
}
};
console.log(getValue('foo.bar.baz')); //true
The first argument of the function is the namespace (dot separated values) and the second one is the parent object, if parent is not provided then window is used.
One more example using the parent argument:
var str = 'foo.bar.baz';
foo = {
bar: {
baz: true
}
};
result = getValue(str, foo);
console.log(result);
Here is an example in jsfiddle.
Similar approach is used in YUI. Their approach is called Namespace pattern. The main benefit is simulation of packages/namespaces. The only difference between this script and the namespace pattern is that the namespace function creates nested structure instead of only returning value.
Try this:
var name = [window].concat('foo.bar.baz'.split('.')).reduce(function(prev, curr) {
return prev[curr];
});
console.log(name);
// -> 'true'
What I can think of is using string split to split that string into an array first, and access the object via [] to access the attribute of that object with a loop

split '{a}{b}{c}'?

I have an object that looks like
var obj = {
a: {
a: {
a: 'value'
}
},
b: {
a: {
a: 'value2'
},
b: {
a: 'value3'
}
}
}
I have a function which gets given a mask that looks like {b}{a}{a} what I want to do is get the value at obj.b.a.a how can I do this?
Slice off the first and last character, split by }{, and then recursively access the object with each element in turn (since foo.bar and foo['bar'] are equivalent).
This will work if your mask always has three properties. If not, you can write a function that does something similar:
var mask = "{a}{b}{c}";
var props = mask.replace(/{|}/g, "");
obj[props[0]][props[1]][props[2]];
obj.getMask = function(s) {
var o=this, attrs=s.slice(1,s.length-1).split("\}\{");
while (attrs.length > 0) {
o = o[attrs.shift()];
if (!o) return null;
}
return o;
};
obj.getMask("{a}{a}{a}"); // => "value"
obj.getMask("{b}{a}{a}"); // => "value1"
obj.getMask("{x}{y}{z}"); // => null
Of course, you can change the signature to pass in "obj" instead of using this if you don't want to muck up the object itself.
You can use reduce rather than use recursion. Here's a one-liner:
function index(obj, indexList) {
return indexList.reduce(function(obj,x){return obj[x]}, obj);
}
function indexWithMask(mask) {
return index(obj, mask.slice(1,-1).split('}{'));
}
Here is my code without using eval. Its easy to understand too.
function value(obj, props) {
if (!props) return obj;
var propsArr = props.split('.');
var prop = propsArr.splice(0, 1);
return value(obj[prop], propsArr.join('.'));
}
var obj = { a: { b: '1', c: '2', d:{a:{b:'blah'}}}};
console.log(value(obj, 'a.d.a.b')); //returns blah
If you are still using the mask part, you can modify a bit on the code.
function value(obj, props) {
if (!props) return obj;
var propsArr = props.match(/\{[a-zA-Z1-9]+\}/g);
var prop = propsArr.splice(0, 1);
return value(obj[prop[0].replace('{', '').replace('}', '')], propsArr.join(''));
}

Categories

Resources