How to shorten this code - javascript

I am having the following plugin:
(function($) {
$.fn.myPlugin = function(options) {
var opt = $.extend({}, $.fn.myPlugin.defaults, options);
if (!opt.a) {
console.log('a is required!');
return false;
}
if (!opt.b) {
console.log('b is required!');
return false;
}
if (!opt.c) {
console.log('c is required!');
return false;
}
//Rest of the logic
}
$.fn.myPlugin.defaults = {
};
});
Now this plugin will be called from outside like this:
$('div.x').myPlugin({
a:'aa',
b:'bb',
c:'cc'
});
As you can see from the plugin I need a, b and c options from outside i.e they are compulsory. But there can 10-15 compulsory options and this code
if (!opt.a) {
console.log('a is required!');
return false;
}
if (!opt.b) {
console.log('b is required!');
return false;
}
if (!opt.c) {
console.log('c is required!');
return false;
}
can become lengthy and cumbersome. Is there any shorter or smarter way to write this? I was thinking of some common code.

If there are that many, you could put them in an array and check that way:
var required = ['a', 'b', 'c'];
var index, optname;
for (index = 0; index < required.length; ++index) {
optname = required[index];
if (!(optname in opt)) {
console.log(optname + " is required");
return false;
}
}
Note that I've gone for an if (!(optname in opt)) check there (rather than if (!opt[optname]) as you originally had), to allow for options that must be specified but for which 0, false, undefined, or other falsey values are valid. The in check sees whether the option is there without worrying about its value being truthy.
Slightly off-topic: You might choose to wait to fail until you've checked for all of the properties, as #Marcus points out in the comments. Also, you might consider throwing an exception rather than returning false, as someone failing to specify the options correctly should be an exceptional condition... But those are minor points.

$.each(['a','b','c'], function(key, val) {
if (!opt[val] !== void 0) console.log(val + " is require");
});
An $.each is just so much shorter/tidier.
It's the same thing as #T.J.Crowder's answer though.
P.S. void 0 === undefined

Related

Call function only once after a map()

I am having an issue on Nodejs, I need to call a function only once when item.IS_RACING === 1
look
_.map(recordsets, function(items) {
return _.map(items, function(item) {
if (item.IS_RACING === 1) {
_this.getRacing();
}
});
});
I have that _this.getRacing(); which is being called everytime the conditional is true, but if there is 20 items with IS_RACING === 1, so the function _this.getRacing(); is going to be call 20 times. I need something like, once the app detects when the first IS_RACING === 1 comes up, then fires _this.getRacing(); only once.
Any recommendation ?
As Pointy pointed out (sorry) in the comments, you really don't want to use map() to do this.
Think of the problem in terms of how you would explain it to another developer.
If any of the record sets has an item that is racing, I want to call getRacing().
Now, write code that represents your intent.
var somethingIsRacing = _.some(recordsets, function(items) {
return _.some(items, function(item) {
return item.IS_RACING === 1;
});
});
if(somethingIsRacing) {
_this.getRacing();
}
This code follows a principle called Command-Query Separation, where you first query to find the information you need using a functional style of programming, then you perform actions that will have side-effects using an imperative programming style.
A flag variable usually does the trick:
var getRacingCalled = false;
_.map(recordsets, function(items) {
return _.map(items, function(item) {
if (item.IS_RACING === 1 && !getRacingCalled) {
_this.getRacing();
getRacingCalled = true;
}
});
});
Try to do it with a closure:
var closure = (function() {
var fired = false;
return function (item) {
if (!fired && item.IS_RACING === 1) {
fired = true;
_this.getRacing();
}
};
})();
_.map(recordsets, function(items) {
return _.map(items, closure(item));
});

Working equivalent for method .some() in javascript or jquery?

Was looking for "equivalent for some method in javascript" and "return just one value if is in array", but saw only the answers to the way in which to determine the type of variables or too many unnecessary.
I bypass all inputs in html and i want something like this:
$('#goodsFilter')
.find('input[type="number"]')
.some(function(i,el){
return (isNumber($(el).val())) ? 1 : 0;
});
But it throws an error:
"TypeError: 'undefined' is not a function" (eg. Safari 6.0.4).
UPD: Error comes from the last line, yeah, where });.
isNumber:
function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); }
This should check for the presence of each input information, and, if at least one of them is not empty, return 1, otherwise 0.
How can I replace it to work in most modern browsers?
UPD:
Problem was solved. I'm a little confused in choosing the answer. The code of #RobG implementation of .some() is more understandable for beginners (and I am) so I switched my vote.
For anyone else who comes to this thread, you can use some() on a jQuery object this way:
$.makeArray($(...)).some(function(x) { ... })
jQuery.makeArray() converts the jQuery object into an Array, so you can use some() on it.
As suggested by #alf-eaton, you could use:
$(…).toArray().some(function(node) { … })
Array.prototype.some returns true or false, so you can do:
.some(function(el){
return !isNaN(el.value);
}
You don't say where the error comes from, is it from the call to isNumber?
Edit
Ah, so your issue is with some.
If you want a jQuery some method, then it should at least mimic the built–in ECMAScript some, which takes two arguments: a callback function and an optional this argument.
The callback function should take three arguments: the value, the index (optional) and an optional value to use as the this argument. It should access the numeric members in ascending order and only visit members that actually exist.
So it should be something like (noting that jQuery.fn === jQuery.prototype):
jQuery.fn.some = function(fn, thisArg) {
var result;
for (var i=0, iLen = this.length; i<iLen; i++) {
if (this.hasOwnProperty(i)) {
if (typeof thisArg == 'undefined') {
result = fn(this[i], i, this);
} else {
result = fn.call(thisArg, this[i], i, this);
}
if (result) return true;
}
}
return false;
}
So if you want now you can do:
var result = $('#goodsFilter')
.find('input[type="number"]')
.some(function(el) {
return isNumber(el.value);
})? 1 : 0;
or you can do either of the following to coerce true to 1 and false to 0:
var result = Number($('#goodsFilter')
.find('input[type="number"]')
.some(function(el) {
return isNumber(el.value);
}));
or
var result = +($('#goodsFilter')
.find('input[type="number"]')
.some(function(el) {
return isNumber(el.value);
}));
The above is only lightly tested, the optional thisArg parameter might be redundant.
You could use the .filter method, and then check the length.
$('#goodsFilter')
.find('input[type="number"]')
.filter(function(i,el){ return isNumber($(el).val())); })
.length > 0
$(...).is(function) should work too. The jQuery API documentation states (emphasis mine):
Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
So using the example in the question, we would have something like:
var result = $('#goodsFilter')
.find('input[type="number"]')
.is(function(idx, el) {
return isNumber(el.value);
})? 1 : 0;
. . In the most basic version, you can just create a "some" function:
function eSome(arr, f) { var i = 0, n = arr.length;
for (;i<n;i++) { if (!i in arr) { continue }
if (f(i, arr[i])) { return true; }
} return false;
}
var list = [0, 1, 2, 3, 4, 5];
var testFunction = function (i, e) { return e === 2; };
console.log(eSome(list, testFunction));
//returns true and the loop ran only for the necessary three times.
. . If you want to chain the .some call in a jQuery object, you can add it as a jQuery function as well, using something like this (now tested and fixed) example:
jQuery.fn.some = function (f) { var i = 0, n = this.length;
for (;i<n;i++) { if (!i in this) { continue }
if (f(i, this[i])) { return true; }
}
return false;
}
$('.a').some(function (i, el) { return ($(el).text() == 'weeee!'); });
. . As #RobG pointed out in the comments, the native Array.prototype.some implementation calls your callback with a different set of parameters. I'm following the OP's sample code, but you can mimic the ECMA implementation's parameter with if (f(this[i], i, this)) { return true; } inside the loop.
. . You can also shim it on Array.prototype.some, but I strongly advise against any direct modifications to the built-in prototypes.
Implementation in Vanilla Javascript( with arrow syntax)
function some(arr,callback){
for(let i=0;i<arr.length;i++){
if(callback(arr[i],i,arr)){
return true;
}
}
return false;
}
some use:
check if an array has an even number:
function hasEvenNum(arr){
return arr.some(value=>{
return value % 2 === 0;
});
}
Try to use [].prototype.call method, the first argument will be an Array-like value, the second one is a function that will be called on each element.
[].some.call($('#goodsFilter').find('input[type="number"]'), function (el) {
return isNumber($(el).val());
});

How to accomplish this without using eval

Sorry for the title but I don't know how to explain it.
The function takes an URI, eg: /foo/bar/1293. The object will, in case it exists, be stored in an object looking like {foo: { bar: { 1293: 'content...' }}}. The function iterates through the directories in the URI and checks that the path isn't undefined and meanwhile builds up a string with the code that later on gets called using eval(). The string containing the code will look something like delete memory["foo"]["bar"]["1293"]
Is there any other way I can accomplish this? Maybe store the saved content in something other than
an ordinary object?
remove : function(uri) {
if(uri == '/') {
this.flush();
return true;
}
else {
var parts = trimSlashes(uri).split('/'),
memRef = memory,
found = true,
evalCode = 'delete memory';
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
memRef = memRef[dir];
evalCode += '["'+dir+'"]';
}
else {
found = false;
return false;
}
if(i == (parts.length - 1)) {
try {
eval( evalCode );
} catch(e) {
console.log(e);
found = false;
}
}
});
return found;
}
}
No need for eval here. Just drill down like you are and delete the property at the end:
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
if(i == (parts.length - 1)) {
// delete it on the last iteration
delete memRef[dir];
} else {
// drill down
memRef = memRef[dir];
}
} else {
found = false;
return false;
}
});
You just need a helper function which takes a Array and a object and does:
function delete_helper(obj, path) {
for(var i = 0, l=path.length-1; i<l; i++) {
obj = obj[path[i]];
}
delete obj[path.length-1];
}
and instead of building up a code string, append the names to a Array and then call this instead of the eval. This code assumes that the checks to whether the path exists have already been done as they would be in that usage.

More efficient Javascript

Looking for another eye on making the following Javascript more efficient.
The following JSON is produced from a Resteasy service:
var testing = {
"com:klistret:cmdb:ci:pojo:successful":true,
"com:klistret:cmdb:ci:pojo:count":1,
"com:klistret:cmdb:ci:pojo:elements":{
"com:klistret:cmdb:ci:pojo:id":123,
"com:klistret:cmdb:ci:pojo:name":"Mars",
"com:klistret:cmdb:ci:pojo:type":{
"com:klistret:cmdb:ci:pojo:id":1,
"com:klistret:cmdb:ci:pojo:name":"Environment"
},
"com:klistret:cmdb:ci:pojo:configuration":{
"#www:w3:org:2001:XMLSchemainstance:type":"Environment",
"#Watermark":"past",
"com:klistret:cmdb:ci:commons:Name":"Mars"
}
}
};
Extended the Extjs JSONReader to handle key depths higher than 2 in the createAccessor method. Wondering if there is a way to make the code more efficient? The function below will be called like function(testing, "com:klistret:cmdb:ci:pojo:configuration.#Watermark") where the com:klistret:cmdb:ci:pojo:elements property is the root.
createAccessor : function(){
var re = /[\[\.]/;
return function(expr) {
if(Ext.isEmpty(expr)){
return Ext.emptyFn;
}
if(Ext.isFunction(expr)){
return expr;
}
# THIS FUNCTION I WANT TO BE EFFICIENT
return function(obj){
while (String(expr).search(re) !== -1) {
var i = String(expr).search(re);
var key = expr.substring(0, i);
if (obj.hasOwnProperty(key)) {
obj = obj[key];
}
expr = expr.substring(i+1, expr.length);
}
return obj[expr];
};
};
}()
This is what I use. I only allow dot annotation, mind:
Ext.override(Ext.data.JsonReader, {
createAccessor: function() {
return function(expr) {
if (Ext.isEmpty(expr)) {
return Ext.emptyFn;
} else if (Ext.isFunction(expr)) {
return expr;
} else {
return function(obj) {
var parts = (expr || '').split('.'),
result = obj,
part,
match;
while (parts.length > 0 && result) {
part = parts.shift();
match = part.match(/^(.+?)(\[(\d+)\])?$/);
result = result[match[1]];
if (result && match[3]) {
result = result[match[3]];
}
}
return result;
}
}
};
}()
});
A basic optimization would be to avoid scanning the string twice with search, which is pretty slow.
The best you could do is replace all the string scanning and substring extraction with a single call to expr.split('.'), which would support accessors of the form aaa.bbb.ccc.ddd and turn them into an array like ['aaa','bbb','ccc','ddd']. The other two characters you seem to support ([ and ]) wouldn't work.
Alternately, you could do an initial match for /[^\].[]+/g over your entire string and keep the matches to obtain a similar array, but this would possibly be slower than the previous solution.

Alternative to jQuery's .toggle() method that supports eventData?

The jQuery documentation for the .toggle() method states:
The .toggle() method is provided for convenience. It is relatively straightforward to implement the same behavior by hand, and this can be necessary if the assumptions built into .toggle() prove limiting.
The assumptions built into .toggle have proven limiting for my current task, but the documentation doesn't elaborate on how to implement the same behavior. I need to pass eventData to the handler functions provided to toggle(), but it appears that only .bind() will support this, not .toggle().
My first inclination is to use a flag that's global to a single handler function to store the click state. In other words, rather than:
$('a').toggle(function() {
alert('odd number of clicks');
}, function() {
alert('even number of clicks');
});
do this:
var clicks = true;
$('a').click(function() {
if (clicks) {
alert('odd number of clicks');
clicks = false;
} else {
alert('even number of clicks');
clicks = true;
}
});
I haven't tested the latter, but I suspect it would work. Is this the best way to do something like this, or is there a better way that I'm missing?
Seems like a reasonable way to do it... I'd just suggest that you make use of jQuery's data storage utilities rather than introducing an extra variable (which could become a headache if you wanted to keep track of a whole bunch of links). So based of your example:
$('a').click(function() {
var clicks = $(this).data('clicks');
if (clicks) {
alert('odd number of clicks');
} else {
alert('even number of clicks');
}
$(this).data("clicks", !clicks);
});
Here is a plugin that implements an alternative to .toggle(), especially since it has been removed in jQuery 1.9+.
How to use:
The signature for this method is:
.cycle( functions [, callback] [, eventType])
functions [Array]: An array of functions to cycle between
callback [Function]: A function that will be executed on completion of each iteration. It will be passed the current iteration and the output of the current function. Can be used to do something with the return value of each function in the functions array.
eventType [String]: A string specifying the event types to cycle on, eg. "click mouseover"
An example of usage is:
$('a').cycle([
function() {
alert('odd number of clicks');
}, function() {
alert('even number of clicks');
}
]);
I've included a demonstration here.
Plugin code:
(function ($) {
if (!Array.prototype.reduce) {
Array.prototype.reduce = function reduce(accumulator) {
if (this === null || this === undefined) throw new TypeError("Object is null or undefined");
var i = 0,
l = this.length >> 0,
curr;
if (typeof accumulator !== "function") // ES5 : "If IsCallable(callbackfn) is false, throw a TypeError exception."
throw new TypeError("First argument is not callable");
if (arguments.length < 2) {
if (l === 0) throw new TypeError("Array length is 0 and no second argument");
curr = this[0];
i = 1; // start accumulating at the second element
} else curr = arguments[1];
while (i < l) {
if (i in this) curr = accumulator.call(undefined, curr, this[i], i, this);
++i;
}
return curr;
};
}
$.fn.cycle = function () {
var args = Array.prototype.slice.call(arguments).reduce(function (p, c, i, a) {
if (i == 0) {
p.functions = c;
} else if (typeof c == "function") {
p.callback = c;
} else if (typeof c == "string") {
p.events = c;
}
return p;
}, {});
args.events = args.events || "click";
console.log(args);
if (args.functions) {
var currIndex = 0;
function toggler(e) {
e.preventDefault();
var evaluation = args.functions[(currIndex++) % args.functions.length].apply(this);
if (args.callback) {
callback(currIndex, evaluation);
}
return evaluation;
}
return this.on(args.events, toggler);
} else {
//throw "Improper arguments to method \"alternate\"; no array provided";
}
};
})(jQuery);

Categories

Resources