Comment passer un paramètre supplémentaire à la fonction de rappel dans la méthode Javascript .filter ()?

104

Je veux comparer chaque chaîne d'un tableau avec une chaîne donnée. Ma mise en œuvre actuelle est:

function startsWith(element) {
    return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith);

Cette fonction simple fonctionne, mais uniquement parce que pour le moment, wordToCompare est défini comme une variable globale, mais bien sûr, je veux éviter cela et le passer en paramètre. Mon problème est que je ne sais pas comment définir startsWith () afin qu'il accepte un paramètre supplémentaire, car je ne comprends pas vraiment comment les paramètres par défaut qu'il prend sont passés. J'ai essayé toutes les différentes façons dont je peux penser et aucune d'elles ne fonctionne.

Si vous pouviez également expliquer comment les paramètres passés aux fonctions de rappel `` intégrées '' (désolé, je ne connais pas de meilleur terme pour ceux-ci) fonctionnent, ce serait génial

agente_secreto
la source

Réponses:

152

Faire startsWithaccepter le mot à comparer et renvoyer une fonction qui sera ensuite utilisée comme fonction de filtrage / rappel:

function startsWith(wordToCompare) {
    return function(element) {
        return element.indexOf(wordToCompare) === 0;
    }
}

addressBook.filter(startsWith(wordToCompare));

Une autre option serait d'utiliser Function.prototype.bind [MDN] (uniquement disponible dans les navigateurs prenant en charge ECMAScript 5, suivez un lien pour un shim pour les navigateurs plus anciens) et de "corriger" le premier argument:

function startsWith(wordToCompare, element) {
    return element.indexOf(wordToCompare) === 0;
}

addressBook.filter(startsWith.bind(this, wordToCompare));

Je ne comprends pas vraiment comment les paramètres par défaut sont passés

Il n'y a rien de spécial à ce sujet. À un moment donné, filterappelle simplement le rappel et passe l'élément courant du tableau. C'est donc une fonction appelant une autre fonction, dans ce cas le callback que vous passez en argument.

Voici un exemple d'une fonction similaire:

function filter(array, callback) {
    var result = [];
    for(var i = 0, l = array.length; i < l; i++) {
        if(callback(array[i])) {  // here callback is called with the current element
            result.push(array[i]);
        }
    }
    return result;
}
Félix Kling
la source
1
D'accord maintenant je comprend. J'essayais de passer les paramètres directement à la fonction de rappel ... J'ai vraiment besoin de travailler sur mon JavaScript. Merci Felix, votre réponse est très utile
agente_secreto
Qu'en est-il du passage d'arguments supplémentaires? J'ai essayé de passer un éventail d'arguments mais cela semble échouer
geotheory
@geotheory: qu'en est-il d'eux? vous passez plusieurs arguments comme à n'importe quelle autre fonction.
Felix Kling
bind (this) après le nom de la fonction avec le chaînage filter () m'a aidé à utiliser .this fonction interne. Merci.
Sagar Khatri
109

Le deuxième paramètre de filtre définira cela à l' intérieur du rappel.

arr.filter(callback[, thisArg])

Vous pouvez donc faire quelque chose comme:

function startsWith(element) {
    return element.indexOf(this) === 0;
}
addressBook.filter(startsWith, wordToCompare);
Jeff
la source
7
J'ai trouvé que c'était la meilleure réponse.
Jeaf Gilbert
alors maintenant, le nouveau tableau sera assigné à l'objet wordToCompare, non? Comment accéder ultérieurement au nouveau tableau à l'aide de l'objet wordToCompare?
Badhon Jain
meilleure réponse. fonctionne parfaitement pour le filtre et la recherche. Et est par documentation WC3 pour les deux: thisValue - Facultatif. Une valeur à transmettre à la fonction à utiliser comme sa valeur "this". Si ce paramètre est vide, la valeur "undefined" sera transmise comme sa valeur "this"
richaa
1
@TarekEldeeb vient de passer un objet que vous faites{one: 'haha', two:'hoho'}
toddmo
2
Ceci est un excellent exemple de la façon dont il peut y avoir de grandes différences entre les réponses quant à la complexité et à quel point elles peuvent être alambiquées par rapport à la façon dont elles peuvent être simples
toddmo
14

Pour ceux qui recherchent une alternative à ES6 utilisant les fonctions fléchées, vous pouvez effectuer les opérations suivantes.

let startsWith = wordToCompare => (element, index, array) => {
  return element.indexOf(wordToCompare) === 0;
}

// where word would be your argument
let result = addressBook.filter(startsWith("word"));

La version mise à jour utilisant comprend :

const startsWith = wordToCompare => (element, index, array) => {
  return element.includes(wordToCompare);
}
jhamPac
la source
Il existe un moyen de passer un paramètre différent de l'élément, de l'index et du tableau? Par exemple, je souhaite transmettre une variable X.
leandrotk
@leandrotk dans ce cas "wordToCompare" est la variable "X" à transmettre.
GetBackerZ
11
function startsWith(element, wordToCompare) {
    return element.indexOf(wordToCompare) === 0;
}

// ...
var word = "SOMETHING";

addressBook.filter(function(element){
    return startsWith(element, word);
});
James Montagne
la source
4

Vous pouvez utiliser la fonction de flèche à l'intérieur d'un filtre, comme ceci:

result = addressBook.filter(element => element.indexOf(wordToCompare) === 0);

Fonctions fléchées sur MDN

Une expression de fonction de flèche a une syntaxe plus courte par rapport aux expressions de fonction et lie lexicalement la valeur this (ne lie pas son propre this, arguments, super ou new.target). Les fonctions fléchées sont toujours anonymes. Ces expressions de fonction sont mieux adaptées aux fonctions non-méthode et ne peuvent pas être utilisées comme constructeurs.

oddRaven
la source
Remarque: non pris en charge sur IE
Luis Lavieri
1

Pour tous ceux qui se demandent pourquoi leur fonction de grosse flèche est ignorée [, thisArg], par exemple pourquoi

["DOG", "CAT", "DOG"].filter(animal => animal === this, "DOG") Retour []

c'est parce thisqu'à l'intérieur de ces flèches, les fonctions sont liées lorsque la fonction est créée et sont définies sur la valeur de thisdans la portée englobante plus large, donc l' thisArgargument est ignoré. J'ai contourné cela assez facilement en déclarant une nouvelle variable dans une portée parent:

let bestPet = "DOG"; ["DOG", "CAT", "DOG"].filter(animal => animal === bestPet); => ["DOG", "DOG"]

Voici un lien vers d'autres lectures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

JKettler
la source
0

basé sur la réponse oddRaven et https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

je l'ai fait de manière différente. 1) en utilisant la manière de fonction. 2) en utilisant la manière en ligne.

//Here  is sample codes : 

var templateList   = [
{ name: "name1", index: 1, dimension: 1 }  ,
{ name: "name2", index: 2, dimension: 1 }  ,
{ name: "name3", index: 3, dimension: 2 }  ];


//Method 1) using function : 

function getDimension1(obj) {
                if (obj.dimension === 1) // This is hardcoded . 
                    return true;
                else return false;
            } 

var tl = templateList.filter(getDimension1); // it will return 2 results. 1st and 2nd objects. 
console.log(tl) ;

//Method 2) using inline way 
var tl3 = templateList.filter(element => element.index === 1 || element.dimension === 2  ); 
// it will return 1st and 3rd objects 
console.log(tl3) ;

Ashish
la source
0

Il existe un moyen simple d'utiliser la fonction de filtre, d'accéder à tous les paramètres et de ne pas trop la compliquer.

À moins que thisArg du rappel ne soit défini sur un autre filtre de portée, il ne crée pas sa propre portée, et nous pouvons accéder aux paramètres dans la portée actuelle. Nous pouvons définir 'this' pour définir une portée différente afin d'accéder à d'autres valeurs si nécessaire, mais par défaut, elle est définie sur la portée à partir de laquelle elle est appelée. Vous pouvez voir que cela est utilisé pour les portées angulaires dans cette pile .

L'utilisation d'indexOf va à l'encontre de l'objectif du filtre et ajoute plus de surcharge. Le filtre traverse déjà le tableau, alors pourquoi devons-nous le parcourir à nouveau? Nous pouvons plutôt en faire une simple fonction pure .

Voici un scénario de cas d'utilisation dans une méthode de classe React où l'état a un tableau appelé items , et en utilisant un filtre, nous pouvons vérifier l'état existant:

checkList = (item) => {  // we can access this param and globals within filter
  var result = this.state.filter(value => value === item); // returns array of matching items

  result.length ? return `${item} exists` : this.setState({
    items: items.push(item) // bad practice, but to keep it light
  });
}
DBrown
la source