Mutually exclusive booleans in javascript object? - javascript

I have a javascript object with 3 booleans:
var obj = {};
obj.isPrimary = true;
obj.isPromotion = false;
obj.isSocial = false;
Only one of these can be true, there can never be a case where more than 1 is true. How can I achieve this?

Using a getter / setter should do the trick:
var obj = {
set isPrimary(bool){
this.Primary = bool;
if(bool){
this.Promotion = this.Social = false;
}
},
set isSocial(bool){
this.Social = bool;
if(bool){
this.Promotion = this.Primary = false;
}
},
set isPromotion(bool){
this.Promotion = bool;
if(bool){
this.Primary = this.Social = false;
}
},
get isPrimary(){ return this.Primary; },
get isSocial(){ return this.Social; },
get isPromotion(){ return this.Promotion; }
}
obj.isPrimary = true;
obj.isSocial = true;
obj.isPromotion = true;
alert(obj.isPrimary + ' ' + obj.isSocial + ' ' + obj.isPromotion);
// false false true (So only `obj.isPromotion` is true)

You can use Object.defineProperty something like:
Object.defineProperty(obj, 'isPrimary', {
get: function(){
return !this.isPromotion && !this.isSocial; //<-- Your logic goes here.
}
});
Of course the logic behind toggling this option on and off it is up to you.

You can simulate this with a function like this
function createToggleValues() {
var options = {
"primary": 1,
"promotion": 2,
"social": 4
},
value = 0;
return {
"set": function(name) {
value = options[name];
},
"is": function(name) {
return (value & options[name]) === options[name];
}
}
};
var togglable = createToggleValues();
togglable.set("primary");
console.log(togglable.is("primary"));
// true
If you require more options to be added, then you might want to simply extend the options object with new values and the next multiple of 2.

Closures are your friend. They prevent that somebody can fiddle with the internal state.
function createPropObj() {
var props = 0;
return {
get isPrimary() { return props === 1; },
set isPrimary(val) { if (val) { props = 1; }; },
get isPromotion() { return props === 2; },
set isPromotion(val) { if (val) { props = 2; }; },
get isSocial() { return props === 4; },
set isSocial(val) { if (val) { props = 4; }; }
}
}
You can use it like that:
> var test = createPropObj();
undefined
> test.isPrimary = true;
true
> test.isPromotion = true;
true
> test.isPrimary
false

You could create an object responsible for allowing a single flag to be true at a time. That would relieve your obj from implementing that logic. In the example below, SingleTrueFlagGroup holds onto a collection of flags, allowing a single one to be true at a time. Your obj can then double-dispatch on an instance of this object to get the job done.
The advantage of a similar implementation is that adding and removing flags becomes trivial.
var flags = ['isPrimary', 'isPromotion', 'isSocial'],
obj = {
_flags: new SingleTrueFlagGroup(flags)
};
flags.forEach(function (flag) {
Object.defineProperty(obj, flag, {
get: function () { return this._flags.get(flag); },
set: function (value) { this._flags.set(flag, value); }
});
});
obj.isPrimary = true;
obj.isPromotion = true;
console.log(obj.isPrimary); //false
console.log(obj.isPromotion); //true
function SingleTrueFlagGroup(flags) {
this._flags = {};
(flags || []).forEach(this.add.bind(this));
}
SingleTrueFlagGroup.prototype = {
constructor: SingleTrueFlagGroup,
set: function (flagToSet, value) {
var flags = this._flags;
(flags[flagToSet] = !!value) && Object.keys(flags).forEach(function (flag) {
if (flag !== flagToSet) flags[flag] = false;
});
},
get: function (flag) { return this._flags[flag]; },
add: function (flag) { this._flags[flag] = false; }
};

The other answers may work, but they seem over-engineered*.
If the values are exclusive, you should represent them in a single property, not three separate booleans:
obj.type = 'primary' // or 'promotion' or 'social'
Assignment among different values is inherently exclusive, since the variable can only hold one value at a time.
Or if you don't like magic strings, you can use an enumeration of defined values:
const TYPE_PRIMARY = 0
const TYPE_PROMOTION = 1
const TYPE_SOCIAL = 2
...
obj.type = TYPE_PRIMARY

Related

Knockotjs Validation. Passing function gives undefined, because of property order inside VM

validation works fine if validation properties are placed after "HasError" property in VM.
In the case that the property placed before HasError I will get "parameters.hasError" as undefined. I think it's because the property "HasError" is not defined to that time.
Is there any solution without changing the order of the properties inside VM to make it work.
Thanks!
self._BusTypeDefault = function(param) {
var ret = param.BusType;
if(typeof(ret)==='undefined') {
ret = '';
}
else if(ko.isObservable(ret)) {
ret = ret.peek();
}
return ret;
};
self.BusType = ko.observable(self._BusTypeDefault(init)).extend({maxLength: {message: $Resources.PCIBUSError(), maxFieldLength: 255,hasError: self.HasError }});
self._HasErrorDefault = function(param) {
var ret = param.HasError;
if(typeof(ret)==='undefined') {
ret = false;
}
else if(ko.isObservable(ret)) {
ret = ret.peek();
}
return ret;
};
self.HasError = ko.observable(self._HasErrorDefault(init)).extend({errorAggregation: {}});
ko.extenders.maxLength = function (target, parameters) {
//add some sub-observables to our observable
target.hasMaxLengthError = ko.observable();
target.validationMessageMaxError = ko.observable();
//define a function to do validation
function validate(newValue) {
var preValue = target.hasMaxLengthError();
if (newValue.length >= parameters.maxFieldLength) {
target.hasMaxLengthError(true);
target.validationMessageMaxError(parameters.message || "This field is required");
}
else {
target.hasMaxLengthError(false);
target.validationMessageMaxError("");
}
if (parameters.hasError != null && target.hasMaxLengthError() !== preValue && typeof preValue !== 'undefined') {
parameters.hasError(target.hasMaxLengthError());
}
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
You can use a function to delay the interpretation of hasError:
this.myObservable = ko.observable(1).extend({ myExtender : { hasError: function () { return self.hasError } } });
Then in the extender you'll need to call the function to actually get the observable behind:
ko.extenders.myExtender = function (target, params) {
function validate(newValue) {
alert("New Value: " + newValue + " ; Has Error: " + params.hasError()());
}
target.subscribe(validate);
}
See this example: http://jsfiddle.net/7ywLN/

How to declare array of specific type in javascript

Is it possible in java script to explicitly declare array to be an array of int(or any other type)?
something like var arr: Array(int) would be nice...
var StronglyTypedArray=function(){
this.values=[];
this.push=function(value){
if(value===0||parseInt(value)>0) this.values.push(value);
else return;//throw exception
};
this.get=function(index){
return this.values[index]
}
}
EDITS: use this as follows
var numbers=new StronglyTypedArray();
numbers.push(0);
numbers.push(2);
numbers.push(4);
numbers.push(6);
numbers.push(8);
alert(numbers.get(3)); //alerts 6
Array of specific type in typescript
export class RegisterFormComponent
{
genders = new Array<GenderType>();
loadGenders()
{
this.genders.push({name: "Male",isoCode: 1});
this.genders.push({name: "FeMale",isoCode: 2});
}
}
type GenderType = { name: string, isoCode: number }; // Specified format
If simply you want to restrict user to push values as per first value entered you can use below code
var stronglyTypedArray = function(type) {
this.values = [];
this.typeofValue;
this.push = function(value) {
if(this.values.length === 0) {
this.typeofValue = typeof value;
this.pushValue(value);
return;
}
if(this.typeofValue === typeof value) {
this.pushValue(value);
} else {
alert(`type of value should be ${this.typeofValue}`)
}
}
this.pushValue = function(value) {
this.values.push(value);
}
}
If you want to pass your own type, you can customize the above code a bit to this
var stronglyTypedArray = function(type) {
this.values = [];
this.push = function(value) {
if(type === typeof value) {
this.pushValue(value);
} else {
alert(`type of value should be ${type}`)
}
}
this.pushValue = function(value) {
this.values.push(value);
}
}

Updating values with a function, returning not working

So I am using a function to update my values, but I can't then get them back. I see values don't get updated, but is there any way of saving them as a reference to the return of the function.
function Amphibia(wheelRadius, finsPerPropeller, propellersSpinDirection, mode) {
this.speed = 0;
this.mode = mode;
var amphibiaWheel = new PropulsionUnits.Wheel(wheelRadius);
var amphibiaPropeller = new PropulsionUnits.Propeller(finsPerPropeller, propellersSpinDirection);
this.changeMode = function () {
if (mode == "land") {
mode = "water";
}
else if(mode == "water") {
mode = "land";
}
return {
mode: mode
}
}
this.accelerate = function() {
if(this.mode == "water"){
this.speed += amphibiaPropeller.acceleration;
}
else if(this.mode == "land"){
this.speed += 4*amphibiaWheel.acceleration;
}
}
this.changePropellerSpinDirection = function() {
amphibiaPropeller.changeSpinDirection();
}
return {
speed: this.speed,
mode: this.mode,
changeMode: this.changeMode,
accelerate: this.accelerate,
changePropellerSpinDirection: this.changePropellerSpinDirection
}
}
So here I am experiencing problems with changing the mode and the changeMode function expression. Mode in it should refer to this.mode and then I should be able to update the value.
mode and this.mode are not the same. In your functions you are checking/setting values on mode and this.mode, separately.
Either should work fine, as long as you're using one or the other, in the same place, the same way.
Edit
var Amphibia = function (wheelRadius, finsPerPropeller, propellersSpinDirection, mode) {
var amphibia = this,
MODES = { LAND : "land", WATER : "water" };
amphibia.getMode = function () { return mode; };
amphibia.setMode = function (val) { mode = val; };
amphibia.changeMode = function () {
amphibia.setMode((mode === MODES.LAND) ? MODES.WATER : MODES.LAND);
};
};
var amphibia = new Amphibia("", "", "", "land");
amphibia.getMode(); // "land"
amphibia.changeMode();
amphibia.getMode(); // "water"
mode is now 100% private, and unique to that instance.
If you don't need it to be, then you can append it to this, if you'd like.
But here's your problem:
var Amphibia = function () {
var amphibia = this,
amphibiaPropeller = new Propeller( );
// mode, getMode, setMode, etc...
amphibia.accelerate = function () {
if (amphibia.getMode() === "water") {
this.speed += amphibiaPropeller.acceleration;
}
};
};
var amphibia = new Amphibia();
var bob = { speed : 0 };
bob.accelerate = amphibia.accelerate;
bob.accelerate();
// if amphibia.mode === "water", bob.speed += amphibiaPropeller.acceleration
bob.speed; // === amphibiaPropeller.acceleration
setTimeout(amphibia.accelerate, 10); // call amphibia.accelerate in ~10ms
// if amphibia.mode === "water", window.speed += amphibiaPropeller.acceleration
window.speed; // === amphibiaPropeller.acceleration
Be consistent in how you refer to things.
Don't mix self and this, unless you intend to get those side-effects...
And unless you have a very, very good reason to do so (like you're building a framework/engine, not the modules/classes of the game/simulation which use the engine; ie: the difference between building jQuery and building something with jQuery), then you should probably avoid doing it.
If you have closure ("private") state that you want to expose to the outside world, all you need is a function that returns that value, and/or one that sets it.
All of a sudden, the differences between self and this and what is which, when, all go away, as long as you are consistent with how you use them, and you know what the value of this is going to be, every time you call the method.
Notice I'm not returning anything...
When I use new, the value of this (amphibia/self) gets returned by default.
If you want to use private values, and return a "Revealing Module" (which is what I typically prefer), then you can simply do this:
var Amphibia = function (mode) {
var getMode = function () { return mode; },
setMode = function (val) { mode = val; },
changeMode = function () {
setMode( mode === "water" ? "land" : "water" );
};
return {
getMode : getMode,
setMode : setMode,
changeMode : changeMode
};
};
var amphibia = new Amphibia("water");
// `new` won't do any harm, but you can also not use it,
// without it saving everything to `window`
amphibia.getMode(); // "water"
amphibia.changeMode();
amphibia.getMode(); // "land"
Or, maybe if you want that to look a little more like a module/component...
return {
mode : { get : getMode, set : setMode, switch : changeMode }
};
var amphibia = Amphibia("land");
amphibia.mode.get(); // "land"
amphibia.mode.switch();
amphibia.mode.get(); // "water"
var bob = { };
bob.switchAmphibiaMode = amphibia.mode.switch;
bob.switchAmphibiaMode();
amphibia.mode.get(); // "land"
setTimeout(amphibia.mode.switch, 10);
setTimeout(function () { console.log(amphibia.mode.get()); }, 20);
// 10ms amphibia.mode.switch();
// 20ms console.log(amphibia.mode.get());
// > "water"
...or whatever other structure you'd like.
You don't need a this at all.
But this is something to be very, very careful with in JavaScript, because the meaning of this changes every time you call a function, and if half of the code uses this and half uses self, you're bound for some surprises.
I managed to find the answer myself! :) So basicly this in the function constructor refers to Amphibia and this in the this.changeMode function expression refers to object window. Therefore we can define a variable self = this; in the constructor, so we can refer to the same thing in the function expression, as in the function. I explained it a bit awful, but here is my fixed code ;)
function Amphibia(wheelRadius, finsPerPropeller, propellersSpinDirection, mode) {
this.speed = 0;
var self = this;
self.mode = mode;
var amphibiaWheel = new PropulsionUnits.Wheel(wheelRadius);
var amphibiaPropeller = new PropulsionUnits.Propeller(finsPerPropeller, propellersSpinDirection);
this.changeMode = function () {
if (self.mode == "land") {
self.mode = "water";
}
else if(self.mode == "water") {
self.mode = "land";
}
}
this.accelerate = function() {
if(self.mode == "water"){
this.speed += amphibiaPropeller.acceleration;
}
else if(self.mode == "land"){
this.speed += 4*amphibiaWheel.acceleration;
}
}
this.changePropellerSpinDirection = function() {
amphibiaPropeller.changeSpinDirection();
}
return {
speed: this.speed,
mode: this.mode,
changeMode: self.changeMode,
accelerate: this.accelerate,
changePropellerSpinDirection: this.changePropellerSpinDirection
}
}

Getting nested obj value

Given the following obj:
var inputMapping = {
nonNestedItem: "someItem here",
sections: {
general: "Some general section information"
}
};
I'm writing a function to get that data by passing in a string "nonNestedItem" or in the nested case "sections.general". I'm having to use an eval and I was wondering if there was maybe a better way to do this.
Here is what I have so far and it works okay. But improve!
function getNode(name) {
var n = name.split(".");
if (n.length === 1) {
n = name[0];
} else {
var isValid = true,
evalStr = 'inputMapping';
for (var i=0;i<n.length;i++) {
evalStr += '["'+ n[i] +'"]';
if (eval(evalStr) === undefined) {
isValid = false;
break;
}
}
if (isValid) {
// Do something like return the value
}
}
}
Linky to Jsbin
You can use Array.prototype.reduce function like this
var accessString = "sections.general";
console.log(accessString.split(".").reduce(function(previous, current) {
return previous[current];
}, inputMapping));
Output
Some general section information
If your environment doesn't support reduce, you can use this recursive version
function getNestedItem(currentObject, listOfKeys) {
if (listOfKeys.length === 0 || !currentObject) {
return currentObject;
}
return getNestedItem(currentObject[listOfKeys[0]], listOfKeys.slice(1));
}
console.log(getNestedItem(inputMapping, "sections.general".split(".")));
You don't need to use eval() here. You can just use [] to get values from an object. Use a temp object to hold the current value, then update it each time you need the next key.
function getNode(mapping, name) {
var n = name.split(".");
if (n.length === 1) {
return mapping[name];
} else {
var tmp = mapping;
for (var i = 0; i < n.length; i++) {
tmp = tmp[n[i]];
}
return tmp;
}
}

what's a proper way to check for null/empty string in js before including to params?

I am building a querystring and want to exclude keys if vals are empty, what's a proper way?
setQueryString: function () {
var keyword = $('#keyword').val();
//how to exclude it if keyword is empty?
var params = {
"keyword": $.trim(keyword)
};
return params;
}
take into account, that I will have 20+ inputs like keyword..trying to avoid lots of IF statements
If you have multiple params and you don't want lots of if statements:
setQueryString: function () {
var params = {
'param1': $.trim($('#param1').val()),
'param2': $.trim($('#param2').val())
}
for (p in params) {
if (params.p == null || params.p == '') {
delete params.p;
}
}
return params;
}
Don't set it if it's empty is all:
var keyword = $.trim($('#keyword').val());
var params = {};
if(keyword) {
params.keyword = keyword;
}
return params;
(edit)
If you have lots of things to check, consider using either a loop:
var items = {
keyword: $.trim($('#keyword').val())
// etc.
};
var params = {};
for(var x in items) {
if(items.hasOwnProperty(x) && items[x]) {
params[x] = items[x];
}
}
return params;
or a function of some kind, for example:
var params = {};
function check(name) {
var value = $.trim($('#' + name).val());
if(value) {
params[name] = value;
}
}
check('keyword');
// etc.
return params;
As an empty string is a falsy value in JavaScript you can simpley check if val() is true:
setQueryString: function () {
var keyword = $('#keyword').val();
if(keyword){
var params = {
"keyword": $.trim(keyword)
};
return params;
}
}
Try something like:
setQueryString: function () {
var keyword = $.trim($('#keyword').val());
var params = {};
if(keyword !== undefined && keyword !== '') {
params.keyword = keyword;
}
return params;
}
I believe you need extend: http://api.jquery.com/jQuery.extend/

Categories