Como passo um parâmetro extra para a função de retorno de chamada no método Javascript .filter ()?

104

Quero comparar cada string em um Array com uma determinada string. Minha implementação atual é:

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

Essa função simples funciona, mas apenas porque agora wordToCompare está sendo definida como uma variável global, mas é claro que quero evitar isso e passá-lo como um parâmetro. Meu problema é que não tenho certeza de como definir startsWith (), então ele aceita um parâmetro extra, porque eu realmente não entendo como os parâmetros padrão que ele usa são passados. Eu tentei todas as maneiras diferentes que posso pensar e nenhuma delas funcionou.

Se você também pudesse explicar como funcionam os parâmetros passados ​​para funções de retorno de chamada 'integradas' (desculpe, não conheço um termo melhor para isso), seria ótimo

agente_secreto
fonte

Respostas:

152

Faça startsWithaceitar a palavra para comparar e retornar uma função que será usada como função de filtro / retorno de chamada:

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

addressBook.filter(startsWith(wordToCompare));

Outra opção seria usar Function.prototype.bind [MDN] (disponível apenas em navegadores com suporte para ECMAScript 5, siga um link para um shim para navegadores mais antigos) e "consertar" o primeiro argumento:

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

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

Eu realmente não entendo como os parâmetros padrão que ele leva são passados

Não há nada de especial nisso. Em algum ponto, filterapenas chama o retorno de chamada e passa o elemento atual da matriz. Portanto, é uma função que chama outra função, neste caso, o retorno de chamada que você passa como argumento.

Aqui está um exemplo de função semelhante:

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;
}
Felix Kling
fonte
1
OK, agora eu entendo. Eu estava tentando passar os parâmetros diretamente para a função de retorno de chamada ... Eu realmente preciso trabalhar no meu JavaScript. Obrigado Felix, sua resposta é muito útil
agente_secreto
Que tal passar argumentos adicionais? Tentei passar uma série de argumentos, mas parece que não deu
certo
@geotheory: e quanto a eles? você passa vários argumentos como qualquer outra função.
Felix Kling
bind (this) após o nome da função junto com o encadeamento filter () me ajudou a usar .this dentro da função. Obrigado.
Sagar Khatri
109

O segundo parâmetro do filtro definirá isso dentro do retorno de chamada.

arr.filter(callback[, thisArg])

Então você pode fazer algo como:

function startsWith(element) {
    return element.indexOf(this) === 0;
}
addressBook.filter(startsWith, wordToCompare);
Jeff
fonte
7
Achei que esta é a melhor resposta.
Jeaf Gilbert
então agora a nova matriz será atribuída ao objeto wordToCompare, certo? Como posso acessar a nova matriz posteriormente usando o objeto wordToCompare?
Badhon Jain
melhor resposta. funciona perfeito para filtrar e localizar. E é por documentação WC3 para ambos: thisValue - opcional. Um valor a ser passado para a função a ser usado como seu valor "este". Se este parâmetro estiver vazio, o valor "undefined" será passado como seu valor "this"
richaa
1
@TarekEldeeb acabou de passar um objeto que você fez{one: 'haha', two:'hoho'}
toddmo
2
Este é um ótimo exemplo de como pode haver grandes diferenças entre as respostas quanto à complexidade e quão complicadas elas podem ser versus quão simples podem ser
toddmo
13

Para aqueles que procuram uma alternativa ES6 usando funções de seta, você pode fazer o seguinte.

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

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

A versão atualizada usando inclui :

const startsWith = wordToCompare => (element, index, array) => {
  return element.includes(wordToCompare);
}
jhamPac
fonte
Existe alguma maneira de passar um parâmetro diferente de elemento, índice e matriz? Por exemplo, quero passar uma variável X.
leandrotk
@leandrotk neste caso, "wordToCompare" é a variável "X" a ser passada.
GetBackerZ
11
function startsWith(element, wordToCompare) {
    return element.indexOf(wordToCompare) === 0;
}

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

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

Você pode usar a função de seta dentro de um filtro, como este:

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

Funções de seta no MDN

Uma expressão de função de seta tem uma sintaxe mais curta em comparação com expressões de função e vincula lexicamente o valor this (não vincula seus próprios this, argumentos, super ou new.target). As funções das setas são sempre anônimas. Essas expressões de função são mais adequadas para funções que não sejam de método e não podem ser usadas como construtores.

oddRaven
fonte
Nota: Não compatível com IE
Luis Lavieri
1

Para quem está se perguntando por que sua função de seta gorda está ignorando [, thisArg], por exemplo, por que

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

é porque thisdentro dessas setas as funções são vinculadas quando a função é criada e são configuradas com o valor de thisno escopo abrangente mais amplo, então o thisArgargumento é ignorado. Eu contornei isso facilmente declarando uma nova variável em um escopo pai:

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

Aqui está um link para mais algumas leituras: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

JKettler
fonte
0

com base na resposta oddRaven e https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

eu fiz isso de 2 maneiras diferentes. 1) usando a maneira de função. 2) usando a maneira inline.

//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
fonte
0

Existe uma maneira fácil de usar a função de filtro, acessar todos os parâmetros e não complicar demais.

A menos que thisArg do retorno de chamada seja definido para outro escopo, o filtro não cria seu próprio escopo e podemos acessar parâmetros dentro do escopo atual. Podemos definir 'this' para definir um escopo diferente para acessar outros valores, se necessário, mas por padrão é definido para o escopo de onde é chamado. Você pode ver isso sendo usado para escopos angulares nesta pilha .

Usar indexOf está anulando o propósito do filtro e adicionando mais overhead. O filtro já está passando pelo array, então por que precisamos iterar por ele novamente? Em vez disso, podemos torná-la uma função pura e simples .

Aqui está um cenário de caso de uso dentro de um método da classe React em que o estado tem uma matriz chamada itens e, usando o filtro, podemos verificar o estado existente:

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
fonte