jquery boolean iterators - javascript

...or what's the proper name for some() and every(). Basically, I'm looking for a function or a plugin that would allow me to write something like:
okay = $("#myForm input").every(function() {
return $(this).val().length > 0
})
or
hasErrors = $(listOfUsers).some(function() {
return this.errorCount > 0;
})
You got the idea.
(Before the what-have-you-tried squad arrives, I googled and found jquery.arrayUtils, but that code doesn't look convincing to me).

A simple, straightforward implementation:
$.fn.some = function(callback) {
var result = false;
this.each(function(index, element) {
// if the callback returns `true` for one element
// the result is true and we can stop
if(callback.call(this, index, element)) {
result = true;
return false;
}
});
return result;
};
$.fn.every = function(callback) {
var result = true;
this.each(function(index, element) {
// if the callback returns `false` for one element
// the result is false and we can stop
if(!callback.call(this, index, element)) {
result = false;
return false;
}
});
return result;
};
With ES5, arrays already provide the methods every and some, so you could achieve the same with built in methods:
okay = $("#myForm input").get().every(function(element) {
return $(element).val().length > 0
});
but it won't work in older IE version without HTML5 shim.

You can do something like this
okay = $("#myForm input").each(function() {
return $(this).val().length > 0
})
okay = $("#myForm input").find('class').each(function() {
return $(this).val().length > 0
})

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

Javascript promise conversion

I'm having a hard time understanding how promises works. I've seen some examples and some of it makes sense. However, when I try to actually use it I get stuck. I have the following example:
(I'm using q and nodejs. getItem() returns a promise, but my function doesn't wait for it.)
function calculate(id, item) {
var calcId = id ? id : getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Based on the examples, I don't see how to do it without code duplication.
Promises are resolved asynchronously, so you can't treat them as synchronous as in your example. What you want to do is coerce the value is a Q promise if needed, then proceed from there, that way you have deterministic behavior and no code duplication.
I'm making some assumptions here about update and insert returning promises and returning them when needed so that calculate itself returns a promise.
function calculate( id, item ) {
return Q( id || getItem(item) ).then(function( calcId ) {
// Code seems weird to be, but it's based on the original example.
// Might want to review this.
if ( !id && calcId ) {
calcId = calcId.Id;
}
return calcId ?
update( calcId ) :
insert( item );
});
}
Don’t duplicate your id checks:
function calculate(id, item) {
var calcId;
if (id) {
calcId = id;
} else {
calcId = getItem(item).Id;
}
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Now make calcId consistently a promise holding an Id:
function calculate(id, item) {
var p;
if (id) {
p = Promise.resolve({ Id: id });
} else {
p = getItem(item);
}
return p.then(function (calcId) {
if (calcId.Id) {
return update(calcId.Id);
} else {
return insert(item);
}
});
}
Where, in the case of Q, Promise.resolve is Q.resolve, or just Q.
Bonus: as a generator!
function calculate(id, item) {
var calcId = id ? id : yield getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Several points :
Promise-wrapped results need to be handled with a function specified as a parameter of a promise method (eg .then(fn))
Q(x) can be used to ensure that x is Promise-wrapped. The operation is transparent if x is already a promise - it won't be double wrapped
you need safety in case both id and item are empty or missing
you need further safety in case calcId is falsy and item was not provided
id versus calcId.Id can be more elegantly catered for.
function calculate(id, item) {
if(!id && !item) {
throw new Error("'calculate(): parameters empty or missing'");
}
return Q(id ? {Id:id} : getItem(item)).then(function(resultObj) {
var calcId = resultObj.Id;
if(calcId) {
update(calcId);
} else {
if(item) {
insert(item);
} else {
throw new Error("'calculate(): `calcId` is falsy and `item` is empty of missing'");
}
}
return calcId; //in case you need to chain calculate(...).then(...)
//(Alternatively, if update() and insert() return promises, then return them instead as per dherman's answer)
});
}
Call as follows :
calculate(myId, myItem).then(function(calcId) {
//(optional) anything else you wish to do with `calcId`
}).catch(function(e) {
console.error(e);
});

Check if a JSON array is empty

I know from the first look it sounds like duplicate question but i don't think it is...
I am receiving back a JSON array as:
var test1 = [] ;
or
var test2 = [{},{},{}] ; //This is empty
I have no problem finding out if test1 is empty.
jQuery.isEmptyObject(test1)
My problem is with the test2...
Please note that in some cases the test2 might return something like:
var test2 = [{"a":1},{},{}] ; //All these are not empty
var test2 = [{},{"a":1},{}] ; //All these are not empty
var test2 = [{},{},{"a":1}] ; //All these are not empty
The above scenarios shouldn't be counted as empty.I've tried to use .length but it's not helping as the length is always 3... Any ideas?
Cheers.
function isArrayEmpty(array) {
return array.filter(function(el) {
return !jQuery.isEmptyObject(el);
}).length === 0;
}
jsFiddle Demo
Passes all of your tests.
A pure JavaScript solution would be to replace !jQuery.isEmptyObject(el) with Object.keys(el).length !== 0
Edit: Using Array.prototype.every
function isArrayEmpty(array) {
return array.every(function(el) {
return jQuery.isEmptyObject(el);
});
}
For those playing at home, a non jQuery solution:
var test2 = [{a: 1},{},{}] ; //This is empty
function isEmpty(val) {
var len = val.length,
i;
if (len > 0) {
for (i = 0; i < len; ++i) {
if (!emptyObject(val[i])) {
return false;
}
}
}
return true;
}
function emptyObject(o) {
for (var key in o) {
if (o.hasOwnProperty(key)) {
return false;
}
}
return true;
}
console.log(isEmpty(test2));
Without JQuery: using Array.filter1 and Object.keys2:
function JSONEmpty(obj){
return !obj.length ||
!obj.filter(function(a){return Object.keys(a).length;}).length;
}
// usage
JSONEmpty([{"a":1},{},{}]); //=> false
JSONEmpty([{},{"a":1},{}]); //=> false
JSONEmpty([{},{},{"a":1}]); //=> false
JSONEmpty([]); //=> true
JSONEmpty([{},{},{}]); //=> true
update 2018 Arrow functions are now supported by all modern browsers, so like himel-nag-rana stipulated, you can also use:
const JSONEmpty = obj => !obj.length || !obj.filter(a => Object.keys(a).length).length;
1 More info
2 More info (links contain shims for older browsers)
I had the same problem, and I come with this solution without jQuery:
function isEmpty(x) {
for(var i in x) {
return false;
}
return true;
}
Pretty simple...
if(jQuery.isEmptyObject(test2[0]) && jQuery.isEmptyObject(test2[1]) && jQuery.isEmptyObject(test2[2])))
// do something
Maybe you could try use function like
function isEmptyObject (test) {
for (var i in test) {
if (!jQuery.isEmptyObject(test[i])
return false;
}
return true;
}
Here's my take: turn the array into a set and check for size.
var myArray = [1,2,3];
var mySet = new Set(myArray);
console.log(mySet.size === 0);
check by looping each values in array and return error
Try
for(i=0;js_array[i]!=null;i++)
{
if(js_array[i]=="")
{
alert("Empty");
}
}

How to shorten this code

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

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