Test JSON object contains the attributes I expect - javascript

I am writing a simple nodejs express route that POST's a JSON object.
As a bit of a node/js newbie, I am curious to know if there is an elegant way of testing that a JSON object contains all the attributes that I expect to be submitted and only those attributes?
e.g. if i have a JSON object like this:
data:{
"a":"somevalue".
"b":"somebvalue",
"c":"somecvalue"
}
I was thinking I could do something like:
if( (data.a) && (data.b) && (data.c) ){
//Proceed and process post
else {
// respond with unacceptable
}
Am just wondering if there is a better way, either in JavaScript or express?

Your code is not completely correct for what you're doing. For example for the possible valid object {a:undefined,b:3,c:5} you'd think it does not have the required attributes but in fact it does. A more correct way (assuming the prototype is also fine) would be:
("a" in data) && ("b" in data) && ("c" in data)
If you'd like a solution that scales nicely for multiple properties:
You can use Array.prototype.every:
if(["a","b","c"].every(function(attr){ return attr in data;})){
It's not shorter, but I'd argue it's more semantic, and it doesn't return false positive for empty strings, null and other 'falsy' values. Of course - you can extract this into a function :)
Here's a fiddle

Related

JSON.stringify and Object.keys produce different results on same object

I have an issue that isn't making any sense to me. I was wondering if any of you could help.
I have a data source object which I use to access REST data through. All the complex asynchronous stuff works fine, but I've come completely unstuck on what should be the very simple task of passing options into the configuration of the data sources.
At the moment, I have this function:
object.addSourceOption = function( model, optKey, optVal ){
if(!_.has(config.sources, model) ){ return this; }
else{
var options = config.sources[model]["options"];
options[optKey] = optVal;
console.log(options[optKey]);
//options = JSON.parse( JSON.stringify( ) );
console.log( "Source Option: " + optKey + ": " + optVal
+" added for model: " + model );
var debugString = JSON.stringify(options);
console.log(debugString);
console.log( Object.keys(options));
}
return this;
};
This function is being called and it's being called with good values as far as I can see. Here's an example of some debug output (in this case, the key value is "post_get" and the value is a function, which is printed):
function (element){
}
restData2.js:189 Source Option: post_get: function (element){
} added for model: Contacts
restData2.js:191 {}
restData2.js:192 ["post_get"]
I don't understand why JSON.stringify and Objects.keys produce different results. I don't understand which to trust to debug the code I'm using, or what could possibly be happening behind the scenes to make the two functions disagree about the code I've written.
Congratulations! You've found one of the subtler parts of JSON.stringify(...) :)
Here's a helpful part from MDN:
If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).
While JSON is convenient because it looks like Javascript, JSON is really a subset of Javascript. Moreover, not all JS objects can be represented in JSON. The full details are available at http://json.org/.
But yeah, in your example, post_get is not showing up in the JSON because properties of type function are not legal in JSON.
Hope this helps!
Aside:
Also, keep in mind that sometimes a JS object can have properties that aren't returned by Object.keys(...) either, because Object.keys(...) only returns the properties that were not inherited from any prototypes (base objects) that the object extends.
the key value is "post_get" and the value is a function
JSON.stringify() won't include properties that refer to functions. Object.keys() will. It's as simple as that.

How to preserve hasOwnProperty on JSON.parsed responseText?

I am a very new developer working on a very simple application as part of my training process - so be gentle.
I have built a function in javascript that accepts arbitrary objects from elsewhere and builds a legal POST request string.
Code:
function postify(oPost){
var out = '';
for (key in oPost){
if (oPost.hasOwnProperty(key) && key >0){
if(oPost[key].value != 'Submit'){
out += '&' + oPost[key].name + '=' + oPost[key].value;
}
}
}
return out;
}
There are many like it, but this one is mine. I elected to use hasOwnProperty as a conditional, as the total list of inherited properties could be really quite long.
One of the objects I would like to pass to this function is a JSON parsed responseText object, which is retrieved like so.
function postData(str){
var http = new XMLHttpRequest();
http.open('POST', 'test.php',false);
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http.setRequestHeader("Content-length", str.length);
http.setRequestHeader("Connection", "close");
http.send(str);
var response = JSON.parse(http.responseText);
responseHandle(response);
}
So, the problem - both of these functions do exactly what they are supposed to do, until my responseHandle function routes the response object to the postify function. Manual checking indicates that all expected properties are in place, but postify() won't concatenate a string because those properties seem to have been inherited.
I am fully aware that I could trivially brute force assign all necessary properties - the handler function would do what it needed to either way. I am also aware that my synchronous XMLHttpRequest is deprecated - right this second, it's what I need, and works fine.
So, then, some questions - is there a way to pass my JSON.parsed object such that hasOwnProperty() == true is maintained? Is there a different property or technique I could or should be using in postify() to look for deliberately set key value pairs? Should I just rig the POST to transmit ALL of the inherited properties of the object I am POSTING to PHP?
The problem isn't with hasOwnProperty, it's with key > 0. Unless oPost is an array, the keys will be strings. When you compare a string with a number, the string is converted to a number. But if the string isn't numeric, the conversion will return NaN, and comparing this with 0 is false.
Your function shouldn't have worked for any object, it doesn't matter if it came from JSON.parse(). When JSON.parse returns an object, all the properties are "own".
The fix is to change
if (oPost.hasOwnProperty(key) && key >0){
to
if (oPost.hasOwnProperty(key)){
Objects restored from JSON serialization have no identity beyond "being a plain object", so all you're getting back is a plain, prototype-less object.
However, instead of solving that issue, let's solve the real problem, the one you're trying to use .hasOwnProperty for, by using some modern JS (but not so modern as to use ES6/ES2015 syntax) instead:
function postify(inputObject) {
var keys = Object.keys(inputObject);
return keys.filter(function(key) {
return inputObject[key].value !== 'Submit';
}).map(function(key) {
var e = inputObject[key];
return '&' + e.name + '=' + e.value;
}).join('');
}
on the first line, we get the object's keys using the build in JavaScript Object.keys function. Then we filter the keys, discarding any key for which oPost[key].value is not the string 'Submit'. Then we build a mapping of ["remaining keys", ...] => ["&thing=whatever", "&thing2=moo", ...], and then we join those things without any glue.
Done, no var out required even!

Javascript class - how to make properties required and optional

I'm building a event tracking api and need to provide a JS class with a list of properties of which some of them will be required and some optional. Also, no new properties should be added.
Once the client instantiates and passes the object based on the class, I will be serializing the object and sending it as query string. I already have this part but not sure how to restrict the client from creating new properties.
How do I go about designing the class in JS?
There are a number of ways of doing the things you're wanting. Required/Optionals are pretty easy with utilities like underscorejs/jquery extend, along with some of underscore's utility methods (map, reduce,etc.).
To prevent an object from actually having additional properties though is a bit trickier. You could run an array of known required/optional properties as a check and do a delete on any property values that don't match your criteria.
Another option is to use something like seal. You read up more about this on mozilla's website here.
So I guess what I'd do is get the required properties working first by doing a sanity check against their existence. Your optionals could be provided and wrapped/unwrapped by using extend.
Finally (and maybe an even better route) would be to force the user to call getter/setter methods like setMyProperty( 'value' ) in order to populate any required/optional property value. This way, you aren't going to have to write a big hairy solution using the tools above.
JS is just funny like that. Neither solution is perfect, but they are both possible solutions. I'm sure there are others too. This is just a part of JS that can be a bit of a pain in the arsonal. Good luck.
>>> Edit <<<
This may not be perfect, but this is what I've done so far for the optional/required properties. I am obviously assuming they would just be passing in a set of properties in the constructor.
var _Required = {
requiredProperty1: null,
requiredProperty2: null,
requiredProperty3: null
};
var _Optionals = {
optionalProperty1: null,
optionalProperty2: null,
optionalProperty3: null
};
var EventTrackerAPI = function( settings ) {
var requiredProp, optionalProp;
this.required = {};
this.optional = {};
for( requiredProp in _Required ) {
if( !settings.hasOwnProperty( requiredProp ) ) {
throw new Error( 'FAILED to create an instance of EventTrackerAPI - Required Property (' + requiredProp + ') Missing!' )
}
this.required[requiredProp] = settings[requiredProp];
}
for( optionalProp in _Optionals ) {
if( settings.hasOwnProperty( optionalProp ) ) {
this.optional[optionalProp] = settings.hasOwnProperty( optionalProp );
} else {
this.optional[optionalProp] = null;
}
}
};
Anyways, I'd probably do something like the above. It isn't perfect yet (since I threw it together in about 10 minutes), but it should be a good start. I would also just not allow access to the private storage either.
When I was ready to actually extract the properties (like when you're ready to serialize), I'd do something similar as is being done in the constructor - just manually go through and grab the things you actually want - everything else would just be discarded this way. As a matter of fact, it may make sense to strip out the optional/required logic and make them methods on the object's prototype (EventTrackerAPI.prototype.extractRequired = function( required ) {.... } for example...)
Why not usign just class with constructor - you will have required properties then

JavaScript cache return value of a function with more than one parameter

I'm going through John Resig's snippets on advanced JavaScript. On #19 he mentions a method to cache the return value of a function. What's the best way to cache the return value of a function that has more than one parameter?
There has to be a much better way than stringify-ing the recieved arguments and using that as the key for the cache object:
function $$(selector, el) {
var cacheKey = JSON.stringify(arguments);
if ($$.cache[cacheKey]) return $$.cache[cacheKey];
return ($$.cache[cacheKey] = NodeListToArray( (el || document).querySelectorAll(s) ));
}
$$.cache = {};
You could use a custom hash function that can operate on objects. But hash functions cause collisions and would require significantly more code than your simple example.
Or you could make the cache n-dimensional, where n is the number of arguments. So essentially this:
function $$(selector, el) {
if ($$.cache[selector] && $$.cache[selector][el])
return $$.cache[cacheKey][el];
// etc.
That assumes that both selector and el are able to be used as object keys. You may need to stringify them in another manner.
Just consider an array element,
JSON (JavaScript Object Notation) works with generic platform, so for easy use you must create a function for your use,
Here, $$.cache[0] is your easy way after reading the cachekey,
If we make thing more easy, we might have security problem later.
I hope this will satisfy your requirement :)

Javascript arrays and Meteor session

I have made an interesting observation. When trying to update an array that is stored in the Meteor session storage, the following code will not propagate the changes:
var tags = Session.get("Tags");
tags.push("a");
Session.set("Tags", tags);
But if I change the first line to use Session.get("Tags").slice(), everything depending on the session will update accordingly. I guess this is due to the fact that Meteor tests some references for equality and therefore does not update anything.
Is there a better way to manage lists stored in the meteor session store?
If I now try to remove an element from the collection (using array.remove() from here), the behavior turns out to be a bit ... of ... I am doing this inside a Meteor template event, the code looks like this:
"click .taglist li" : function(e) {
var tags = Session.get("Tags").slice();
var index = cardTags.indexOf(this);
Meteor._debug(Session.get("Tags").slice().indexOf("a"));
Meteor._debug("Removing tag \"" + this + "\", index: " + index, ", typeof(this) = " + typeof(this).toString());
tags.remove(index);
Session.set("Tags", tags);
}
This outputs:
1
Removing tag "a", index: -1, typeof(this) = string
So somehow, the cardTags.indexOf(this); statement seems to return -1 for almost any case. I guess I am doing something fundamentally wrong, as I am quite now to javascript, but somehow I can not figure out whats going on here.
Why will those two calls to indexOf() behave different?
I believe this is the same as this situation in Backbone.js. In order for the change event to be triggered, Meteor needs to have a new reference for the array, not just an updated copy of the old one.
In brief, in order to have the 'correct' behaviour, you'll need to clone the array, make the changes you want, and then do Session.set('foo', myCopiedArray).
In short: Use var index = cardTags.indexOf(this.toString()); instead.
Long version:
When using strings in JavaScript, those are strings, whereas typeof 'test' returns string.
Let's take a look at the following code in order to get find out another way to represent strings in JavaScript:
var func = function () {
return this;
};
console.log(func.call('test'));
The console (at least FireBug) won't show us "test", but instead it shows String {0="t", 1="e", 2="s", 3="t" }. typeof would return "object".
The content of the this statement seems to need to be an object. In order to convert a string into a "String" object we can do console.log(new String('test'));, which is the same as the previously logged value.
To convert a string object into a string (data type), just use its prototype toString.

Categories