javascript build filter function at runtime - javascript

While I go into my use-case in (hopefully not too much) detail below, keep in mind that the fundamental question is "How do I create a customized function at runtime in javascript?"
I have (potentially large) arrays of objects, and the user can build custom searches on those objects. The search function is passed an array of filters of the form
[{field:'name', predicate:'contains', modifier:'Joe'},
{field:'type', predicate:'is', modifier:'Boss'}]
which in this example would return all bosses named Joe.
Toward that end, I created a filtering function that applies the filters to the master list, which looks something like:
var matches = everythingOfThatType;
whereClause.forEach(function(filter) {
switch(filter.predicate) {
case '=':
case 'is':
matches = matches.filter(function(record) { return record[filter.field] == filter.modifier; });
console.log(filter, matches);
break;
case '!=':
case 'is not':
matches = matches.filter(function(record) { return record[filter.field] != filter.modifier; });
console.log(filter, matches);
break;
...
and so on.
It's working all right, but now I'm getting some gnarly complexity (special rules for filter combinations, recursive filtering on array properties, etc.), and for performance it would be better to only iterate through the list of all candidates once anyway.
my ideal solution would look something like
var filterFunc = magicallyCreateFilterFunc(filters);
var matches = everythingOfThatType.filter(filterFunc);
where magicallyCreateFilterFunc() would have something like the original switch statement, but instead of applying the filter, would add a line to the function that would eventually be applied to all the objects. Then I can add all the other complexity and recursion and whatnot in a tight, efficient manner.
It seems to me that JavaScript is well-suited for this sort of task (as an aside, the deeper I get into JavaScript the more I appreciate its depth), but I'm a little stuck on step 1: dynamically define a function based on data at run-time. I would really appreciate a nudge in the right direction.

It turns out there is a simple, clean way to do this. What I didn't know when I asked this question oh, so long ago is that closures and filters are best pals.
Rather than apply the filters in the switch statement, I can add the filter functions to an array, then use a closure to execute all the functions on each member of the array.
So my switch statement above looks more like
var buildFilterList = function(whereClause) {
var filterFunctions = [];
whereClause.forEach(function(filter) {
switch(filter.predicate) {
case '=':
case 'is':
filterFunctions.push((function(field) { return function(record) { return record[filter.field] == filter.modifier; })})(field));
break;
...
Which gives me a list of functions to apply to each element, each in a closure that contains the field it should be applied to. More complex filter functions could have more parameters. Now, how to efficiently take that list of filters and apply them? Closures again.
var filterApplicator = function(filters) {
return function(item) {
var passed = true, i = 0, filterCount = filters.length;
for (i = 0; passed && i < filterCount; i++) {
passed = filters[i](item);
}
return passed;
}
}
var filterFunctions = buildFilterList(whereClause);
matches = everythingOfThatType.filter(filterApplicator(filterFunctions));
filterApplicator() returns a function that will be applied to each element in the original array. That function is in a closure that includes the array of filter functions, so all it has to do is loop through those functions and apply them on the element until one fails.
(It should be noted that I have not tested this exact syntax, but the basic concept is what I wanted to pass on here.)

Related

Iterating over Linked List using Functional programming paradigm

I have a simple Linked list in my project. The project uses underscore.js. I am trying to think of a way to iterate over the linked list using functional programming techniques. Currently, I have the following:
while(cell.next) {
nxt = cell.next;
//check if next cell fulfills some condition. If condition is met,
// loop breaks
}
Is there any way to implement the above in a functional manner (using underscore.js library)?
Thanks in advance.
Not sure how underscore would factor in. Doesn't really seem necessary to get functional style code.
function doCell(c) {
if (!c || your_condition_is_met)
return c;
return doCell(c.next);
}
var result = doCell(cell);
Not sure what the result is supposed to be, so I just returned the current cell if the condition is met.
You could abstract some of it away into a reusable function if you wish.
function nextWhile(v, callback) {
if (!v || callback(v))
return v;
return nextWhile(v.next, callback);
}
Then use it like this:
var result = nextWhile(cell, function(cell) {
return your_condition_is_met;
});
And this lets you create reusable functions.
function cell_is_something(cell) {
return your_condition_is_met;
}
Which cleans up and documents your code.
var result = nextWhile(cell, cell_is_something);

Maybe monad in JavaScript

In the examples for monads.maybe on npm we have:
function find(collection, predicate) {
for (var i = 0; i < collection.length; ++i) {
var item = collection[i]
if (predicate(item)) return Maybe.Just(item)
}
return Maybe.Nothing()
}
Can someone explain what Maybe.Just(item); and Maybe.Nothing() are actually doing?
Put another way; are monads essentially objects used as return values that implement specific interfaces that enable the definition of a sequence of function invocations?
Maybe is used to represent an operation that might fail.
In the case of this function, you return Just(the element) if an element fulfills the predicate, else, you return Nothing to show that it had "failed" (in this case, none of the elements fulfill the predicate).
It's preferred to just returning a null because the return type explicitly shows that it can fail, and the answer can be pattern matched against.
Monads are abstract containers with an API to operate on the data contained within. In the instance of the Option monad I think of it as a giftbox that either has a gift or is empty. Wrapping your data in a Maybe.Just() signifies that this container does infact contain data, while at the same time it maintains the returned value as a Maybe. The caller of your find() method can then do this:
var userPredicate = function(user) { return user.name === 'billy bob'; };
var users = collections.getUsersCollection();
var maybeData = find(users, userPredicate);
if(maybeData.isJust()) {
// there was data...do something with it
} else {
// no data...do something else
}
On the other hand, Maybe.Nothing() indicates the absence of data (the else part in the example above). Ideally, you would wrap your data within like so: var maybeData = Maybe(data) and then operate on this, pass it around etc. This is a signal to anyone receiving this object that they need to handle the case of missing data consciously.
Disclosure: I'm working on a similar library called Giftbox that has a richer API. Take a look at the readme there for some more explanations to help you understand what the Option monad is and how to use it effectively.
Here's an article describing Monads, Applicatives and Functors that might be useful to you.

Detecting how many different types of values in in an array

I know I could use variations of this answer to find out how many different types of data there are in an array:
How to count the number of certain element in an array?
But, what I am looking for is whether there is a simple way to count how many different types of data there are in an array:
I have arrays which may have 0, 1, or 2 as values
so it could be:
a = [1,2,0,1,1,1];
or they may all be the same and a different length:
a = [1,1,1,1,1,1,1,1,1,1,1,1,1];
In javascript, I would like a function that returns "1" if all values are the same, "2" if there is a mixture of just two of the possible values, and if the array contains all three values, "3".
Any help appreciated. Thanks.
The simple approach is to keep a map of found values, and remember every time you add to it: Live Example | Live Source
function countUniques(a) {
var valuesSeen = {};
var count = 0;
a.forEach(function(value) {
if (!valuesSeen[value]) {
++count;
valuesSeen[value] = true;
}
});
return count;
}
(Note: That uses ES5's forEach. If you're using an old browser, you'll need to shim that, search for "ES5 shim" to find a shim for it.)
Or if you don't like the people you work with very much and love using operators instead of branching statements: Live Copy | Live Source
function countUniques(a) {
var valuesSeen = {}, count = 0;
a.forEach(function(value) {
valuesSeen[value] || (++count, valuesSeen[value] = true);
});
return count;
}
Just for fun, here's a "funkier" (and somewhat obfuscated) solution using .reduce that requires no local variables:
function countUniques(a) {
return a.reduce(function(p, v) {
p.c += !(v in p.s); p.s[v] = 1; return p;
}, {c:0, s:{}}).c;
}
It's functionally identical to TJC's answer, except that the valuesSeen and count values are passed around as an object p as the "previous" value passed from the prior iteration of .reduce. The p.c element is equivalent to TJC's count and p.s is valuesSeen.
Note that .reduce (like .forEach) is an ES5 function which will require a shim on older browsers.

Javascript return position index of "matched" array within array

Is there an alternative, faster method of returning the position/index of part of an array within another array (where multiple values match)? It's called a lot within my pathfinding algorithm so could do with being as fast as possible.
My current function is:
// Haystack can be e.g. [[0,1,278.9],[4,4,22.1212]]
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
// Needle of [0,1]: returns 0
// Needle of [4,4]: returns 1
// Needle of [6,7]: returns false
Edit:
I've been messing around a bit and come up with a (rather ghastly) string manipulation-based method (thereby avoiding the costly for loop). I think it's still slightly slower. Could anybody benchmark these methods?
function coordinate_location_in_array(needle,haystack) {
var str1 = ':' + haystack.join(':');
var str2 = str1.replace(':'+needle[0]+','+needle[1],'*').split('*')[0];
if(str2.length == str1.length) return false;
var preceedingElements = str2.match(/:/g);
return preceedingElements!=null?preceedingElements.length:0;
}
Perhaps with some improvements this second method might provide some performance gain?
Edit 2:
Bench marked all 3 described methods using jsperf.com (initial method is fastest):
http://jsperf.com/finding-matched-array-within-array/3
Edit 3:
Just replaced the for(..in..) loop with a for(..;..;..) loop (since I know that the haystack array will never have "gaps") and performance seems to have significantly improved:
function coordinate_location_in_array(needle,haystack){
for(var n=0;n<haystack.length;n++){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
I've updated the jsperf page to include this latest method.
If the "haystack" isn't sorted then there isn't a way to make it faster. Not knowing how the elements in a collection are ordered makes finding something out of it linear by nature, because you just have to check each thing.
If you are using this function over the same "haystack" over and over, you could sort the collection, and use the sorting to make it quicker to find the "needle" (look up different sorting and search algorithms to find one that fits your need best, such as using binary search to find the "needle" after haystack is sorted.)
i don't know if its faster, but you can do something like:
[1,2,3,4].slice(0,2).toString() == [1,2].toString()
in your case it would be:
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n].slice(0,2).toString() == needle.toString()) return n
}
return false;
}
Also found this post, which covers comparison of JS arrays: compare-two-arrays-javascript-associative
Cheers
Laidback
Using a for(..;..;..) loop rather than a for(..in..) loop made the biggest difference.
(See Edit 3 at the end of the question)
Seems to me this is just a substring search but with numbers instead of characters being the components of the string. As such, Boyer-Moore could be applicable, especially if your needles and haystacks get big.

Extremely annoying JavaScript array/object error

Basically, I am rewriting part of one of my web applications. I had a script that would collapse some or all panels of the interface at once, and another to code them.
However, my old functions looked really ugly, and were annoying to type and not powerful enough:
function collapse_all()
{
document.getElementById("panel_1").style.display="none"
document.getElementById("panel_2").style.display="none"
document.getElementById("panel_3").style.display="none"
}
function expand_all()
{
document.getElementById("panel_1").style.display=""
document.getElementById("panel_2").style.display=""
document.getElementById("panel_3").style.display=""
}
Now I have this:
function panel() //first variable in argument is collapse or expand, all others are panels to act on
{
var panels = panel.arguments
alert(typeof panel.arguments)
var mode = panels.shift() //here's my problem
if(mode=="collapse") {mode="none"}
if(mode=="expand") {mode=""}
var items = panels.length
for (i = 0;i < items;i++) {document.getElementById(panels[i]).style.display=mode}
}
panel("collapse","panel_1","panel_2","panel_3")
I have a problem though. Firebug tells me panels.shift() is not a function. With some Googling I managed to find out that panel.arguments isn't an array but an object, so I can't use array methods on it. I'm just really confused as to how I could either convert the object into an array or find another workaround, as I know next to nothing about JavaScript objects. Some example code would be highly appreciated.
You can convert the arguments object into an array like this:
var argsArray = Array.prototype.slice.call(arguments);
What this does is use the slice method common to all arrays via Array.prototype to create a genuine Array object from the array-like arguments. call() (a method of all functions) is used to call this slice method with a this value of arguments and no parameters, which has the effect of copying all of the elements of this into a new array. This may seem devious or hacky but it is actually designed into the language: see the note at the bottom of section 15.4.4.10 of the ECMAScript 3rd Edition spec.
Also, within a function you are provided the arguments object as a variable, so you don't need to access it as a property of the function object as you are doing. In your case, just use arguments rather than panel.arguments.
You could keep it much simpler (cleaned up your formatting, semi-colons, etc.):
function panel()
{
var panels = Array.prototype.slice.call(arguments);
var displayMode = (panels[0] == "collapse" ? "none" : "");
for (var i = 1; i < panels.length - 1; i++)
{
document.getElementById(panels[i]).style.display = displayMode;
}
}
Also, if you're rewriting your application, it might be a good time to consider using things like jQuery. You could assign each one of your panels a certain class name, and reduce your code to something like this:
function panel(hide)
{
$('.className').css({ display: (hide ? 'none' : '') });
}
which you could use like so:
panel(true); // or
panel(false);
Or, because now it's so syntactically simple, you might as well just create two separate functions so that your code is straightforward and you know exactly what it's going to do from the function names alone:
function showPanels() {
$('.className').css({ display: '' });
}
function hidePanels() {
$('.className').css({ display: 'none' });
}
And finally, if you don't worry about doing it via CSS, you could really shorten your script to this, which can't be any clearer:
function showPanels() {
$('.className').show();
}
function hidePanels() {
$('.className').hide();
}
Cheers!

Categories

Resources