I'm using the filter() array helper going through some objects in an array. My idea is to create a dynamic filtering function to go through the objects in the array using bind(), but the arguments in bind are being used in a different way that I expected. Here's the code:
var products = [
{name:"lettuce", type:"vegetable"},
{name:"apple", type:"fruit"},
{name:"carrot", type:"vegetable"},
{name:"orange", type:"fruit"}
];
// this is the function used in filter()
function filterProducts(cat, product){
return product.type === cat;
}
// new array
var vegetables = products.filter(filterProducts.bind(products, "vegetable"));
I'm assuming that the filter helper is passing each object in the array after the arguments in the bind method, so first is the products which accounts for this
in the callback, then is the type I want to check on each object and finally the object itself.
The question is: Would you remend doing it like this? I mean, could this be considered a good practice or would it be better to create a function to filter each type instead of doing it like this?
I'm using the filter() array helper going through some objects in an array. My idea is to create a dynamic filtering function to go through the objects in the array using bind(), but the arguments in bind are being used in a different way that I expected. Here's the code:
var products = [
{name:"lettuce", type:"vegetable"},
{name:"apple", type:"fruit"},
{name:"carrot", type:"vegetable"},
{name:"orange", type:"fruit"}
];
// this is the function used in filter()
function filterProducts(cat, product){
return product.type === cat;
}
// new array
var vegetables = products.filter(filterProducts.bind(products, "vegetable"));
I'm assuming that the filter helper is passing each object in the array after the arguments in the bind method, so first is the products which accounts for this
in the callback, then is the type I want to check on each object and finally the object itself.
The question is: Would you remend doing it like this? I mean, could this be considered a good practice or would it be better to create a function to filter each type instead of doing it like this?
Share Improve this question edited Jul 29, 2016 at 17:51 JJJ 33.2k20 gold badges94 silver badges103 bronze badges asked Jul 29, 2016 at 17:47 RodrigoRodrigo 1,6981 gold badge16 silver badges32 bronze badges 6- Why do you want to use the bind function exactly? – Jean-François Beauchamp Commented Jul 29, 2016 at 17:57
- this is kinda a matter of opinion... – lonewarrior556 Commented Jul 29, 2016 at 17:58
-
why binding
products
? it is already there forfilter
callback function as third argument. – Mr_Green Commented Jul 29, 2016 at 18:00 - 3 If the argument for self reference isn't being used, for clarity it's better to set it to null – lonewarrior556 Commented Jul 29, 2016 at 18:00
- @Jean-FrançoisBeauchamp The idea is use just one function to filter the array. In real life the objects in the array have several keys. But using filter(myFunc) just passes the object to the function and not the key I want to check for. – Rodrigo Commented Jul 29, 2016 at 18:03
5 Answers
Reset to default 10Consider using a factory function instead:
var products = [
{name:"lettuce", type:"vegetable"},
{name:"apple", type:"fruit"},
{name:"carrot", type:"vegetable"},
{name:"orange", type:"fruit"}
];
function filterProducts(category) {
// this is the function used in filter()
return function filter(product) {
return product.type === category;
};
}
// new array
var vegetables = products.filter(filterProducts("vegetable"));
console.log(vegetables);
I would remend this pattern over using bind
, as it's a little easier to follow, at least in my opinion.
Clarification
If you intend to use filterProducts
solely as a factory for Array#filter()
, then congratulations, you've finished reading this answer. If you're plaining that the following is "icky":
// product to be validated
var product = {name:"lettuce", type:"vegetable"};
// validate that product is a vegetable
if (filterProduct('vegetable')(product)) {
// code
}
Then keep reading.
Factory functions are good for defining a class of functions that differ by one or two key variables. In this case the key variable is category
. If you want a one-off function that looks nice in a single line of code, you're gonna be hard-pressed to find one unless you're a lodash
aficionado or something... but for this scenario, consider this instead of the above "icky" block of code:
// product to be validated
var product = {name:"lettuce", type:"vegetable"};
// vegetable validator
var isVegetable = filterProduct('vegetable');
// validate that product is a vegetable
if (isVegetable(product)) {
// code
}
Sure, bind
can be good in some situations, and I'm definitely not suggesting you avoid it at all costs, but you should first ask yourself whether it's really necessary or clean to use it before resorting to it.
This is really a matter of opinion. Some people use bind more than others. With es6 arrow notation, writing the function in the callback will prob be preferred over binding.
Only note I would make is that for clarity, when using bind,
var sayName = function(name){
console.log(name)
}
var sayTony = sayName.bind(null,'Tony')
It's best practice to put null
in as the first argument if it's not being used inside the function.
If you really value readability you can be more explicit and declare another function.
function filterProducts(cat, product){
return product.type === cat;
}
var filterVegtables = filterProducts.bind(null, 'vegtable');
then you can do
var vegetables = products.filter(filterVegtables);
Are you interested in something like this ?
var products = [
{name:"lettuce", type:"vegetable"},
{name:"apple", type:"fruit"},
{name:"carrot", type:"vegetable"},
{name:"orange", type:"fruit"}
];
// this is the function used in filter()
function filterProducts(cat){
return this.type === cat[0];
}
// new array
var veggies = products.filter(function(value){
return filterProducts.bind(value, ['vegetable'])();
});
console.log(veggies);
I still used the bind, but it does bee more of a two liner. Now our value is this (so each element in the array), and our param (cat) is an array, which is why I grab [0] of it.
http://codepen.io/anon/pen/KrBaLB
The Array.filter() method isn't that magic, it will only iterate over the array and call the function for each entry. Don't get me wrong- I'm a fan of elegant solutions, but what you're trying to do is nosense. What you schuld do is (this code is untested and for explanation);
function filterCondition(obj, arg1, arg2, arg3,.. ) {
//TODO: define your individual filter logic
}
var filter = function(products, arg1, arg2, arg3,.. ) {
var filtered = [];
for (var i= 0, l = products.length; i < l; i++) {
var product = products[i];
if (filterCondition(product, arg1, arg2, arg3,.. ))
filtered.push(product);
return filtered;
}
Using bind() means, executing the function in a scope that was defined during bind.. 1st argument. So if you need to bind this function, you can use it:
filter.bind(products, arg1, arg2, arg3,..);
But this means, that the filter function gets executed in the scope of the products-Array. So you have to modify the parameters:
var filter = function(arg1, arg2, arg3,.. ) {
var filtered = [];
for (var i= 0, l = this.length; i < l, i++) {
var product = this[i];
if (filterCondition(product, arg1, arg2, arg3,.. ))
filtered.push(product);
return filtered;
}
btw: Jean-François used this on the array-entry.. that's not wrong, just looks a little bit confusing
I believe this would give the result you want:
var products = [
{name:"lettuce", type:"vegetable"},
{name:"apple", type:"fruit"},
{name:"carrot", type:"vegetable"},
{name:"orange", type:"fruit"}
];
// this is the function used in filter()
function filterProducts(product) {
return product.type === this.toString();
}
// new array
var vegetables = products.filter(filterProducts, "vegetable");
but I am not using the bind function.