0

I have two function that filter an array of strings. The first function filters it according to length and the other function according to the char it contains.

The array of string is loaded on page load.

How to make this functions work together, so when I filter the array according to the char it contains, and I have the appropriate strings, later on I should press the min value, and it should show me the remaining strings from the previous filter. I don't want to load the array again.

Here I have the first function http://jsfiddle.net/arunpjohny/03avr1vh/2/

How to add the other and make them work together .

Myscript:

function filter() {
    var a = document.getElementById('A').checked,
        e = document.getElementById('E').checked,
        o = document.getElementById('O').checked,
        result2; //make a copy

    result2 = animals.filter(function (value) {
        value = value.toLowerCase();
        return (!a || value.indexOf('a') == -1) && (!e || value.indexOf('e') == -1) && (!o || value.indexOf('o') == -1);
    })
    document.getElementById("demo").innerHTML = result2;
}
filter();
Besa
  • 517
  • 1
  • 8
  • 19

3 Answers3

1

You can chain the length filter after the letter filter:

result2 = animals.filter(function (value) {
    // letter filter
}).filter(function (value) {
    // length filter
});

A quick sample.

Honore Doktorr
  • 1,585
  • 1
  • 13
  • 20
  • I want to filter the string with according to min and max value. And it should get updated when I click on the submit button – Besa May 12 '15 at 23:24
  • You can put whatever criteria you want in that filter function, and intercept the form submission, for example http://jsfiddle.net/njubcxyk/1/ Also, note that @Nikos M.’s approach is better for maintainability, I’m just giving quick examples… – Honore Doktorr May 13 '15 at 00:29
1

or you can use some functional programming and do a curry of two functions like this:

function compose(f1, f2)
{
   return function(val) {
      return f1(val)&&f2(val);
   }
}
// in this case the .filter traverses the array only once, 
// instead of twice, i.e once for each subfilter function
result2 = animals.filter(compose(letterFilter, lengthFilter));

jsfiddle example

UPDATE if in understand correctly and you need to filter again (by another filter) at a later time, you will need to store the intermediate results from previous filter and use these as input to the other filter, or just filter the whole array again with a composed filter (as in this example).

The best approach depends on the size of the array and code modularity and independence you would like to achieve.

Nikos M.
  • 8,033
  • 4
  • 36
  • 43
  • Ok I am trying to do that but no success. Lets do it similar as the example you showed me. But I want the second filter to be done for min and max value and it should be triggered on click. – Besa May 12 '15 at 23:31
  • ok, either you will filter the whole array again using a new composed filter (as in this example) and the (new) filtering will be triggered on click, or you will have to store intermediate result from previous filter and use it as input to new filter (again on click), the best approach depends on the size of the array and code modularity and independence you would like to achieve – Nikos M. May 12 '15 at 23:33
  • and in my code I get the error result2 is not defined – Besa May 12 '15 at 23:36
1

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:

  1. getting the checked inputs, converting that to an array of strings.
  2. filtering the animals based on the checked inputs

Each one reads like a little story: (note to read the compose blocks in reverse).

getSwitches

  1. call getElementById on each of letters
  2. filter out the unchecked inputs
  3. get the value for each input
  4. store the resulting array as switches

_filter

  1. split each animal name into an array of letters
  2. filter each animal by
    • convert animal to uppercase
    • animal should not match any of switches
  3. rejoin each animal name to a string
  4. 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!

Community
  • 1
  • 1
Mulan
  • 129,518
  • 31
  • 228
  • 259