jsfiddle demo
After you have a little FP lib built up for yourself, you can write your filter in a declarative way
var animals = ["Bear", "Mouse", "Cat", "Tiger", "Lion"];
var letters = ["A", "E", "O"];
function showAnimals() {
var getSwitches = compose([
map(prop("value")),
filter(prop("checked")),
map(getElementById)
]);
var switches = getSwitches(letters);
var _filter = compose([
map(join("")),
filter(compose([
not,
any(flip(elem)(switches)),
map(toUpperCase)
])),
map(split(""))
]);
var result = _filter(animals);
getElementById("demo").innerHTML = join(", ")(result);
};
Your two main actions are:
- getting the checked inputs, converting that to an array of strings.
- filtering the animals based on the checked inputs
Each one reads like a little story: (note to read the compose blocks in reverse).
getSwitches
- call
getElementById
on each of letters
- filter out the unchecked inputs
- get the
value
for each input
- store the resulting array as
switches
_filter
- split each animal name into an array of letters
- filter each animal by
- convert animal to uppercase
- animal should not match any of
switches
- rejoin each animal name to a string
- store the resulting array as
result
OK, so that's a lot of functions we used. I'm not going to go into each one in detail, but most FP libs would provide these basics for you. I go into some length detail with building some of these from scratch in this post about function composition.
Before we go further!
"Is it performant?"
Yup, but compared to what? If you write a single map
or filter
lambda, you'll likely be able to process the same information a lot faster.
"So this is slow?"
Nope, but only you can be the judge of that. It's still going to arrive at the answer in a tiny fraction of a second, so it's plenty fast for me.
"Why is this better?"
Well for one, it's a lot more declarative. That is, we're telling the computer what we want, and not necessarily telling it how to give us the answer. The latter is a more imperative style.
Things like setting variables, calling for-loops, etc, are telling the computer how to do it's job. Imperative style isn't bad, it's just it can be bad when it gets overly verbose.
Notice we were able to solve the problem by defining two variables: switches
and result
. Everything else is just a function.
For more information, I encourage you to read the other SO answer I linked above.
For those of you interested, here's the functions I used to make the above solution work
var id = function(x) {
return x;
};
var getElementById = function(x) {
return document.getElementById(x);
};
var neq = function(y) {
return function(x) {
return x !== y;
};
};
var not = function(x) { return !x; };
var prop = function(x) {
return function(elem) {
return elem[x];
};
};
var indexOf = function(y) {
return function(x) {
return x.indexOf(y);
};
};
var elem = function(y) {
return compose([neq(-1), indexOf(y)]);
};
var map = function(f) {
return function(xs) {
return xs.map(f);
};
};
var filter = function(f) {
return function(xs) {
return xs.filter(f);
};
};
var any = function(f) {
return function(xs) {
return xs.some(f);
}
};
var reduceRight = function(f) {
return function(i) {
return function(xs) {
return xs.reduceRight(uncurry(f), i);
};
};
};
var toUpperCase = function(s) {
return s.toUpperCase();
};
var split = function(y) {
return function(x) {
return x.split(y);
};
};
var join = function(y) {
return function(x) {
return x.join(y);
};
};
var flip = function(f) {
return function(y) {
return function(x) {
return f(x)(y);
};
};
};
var uncurry = function(f) {
return function(x, y) {
return f(x)(y);
};
};
var compose = flip(reduceRight(flip(id)));
And that's all, Folks!