Executando verificação de segurança...
2

Não tinha pensado desse modo, ótima sua implementação! Eu reescrevi ela de uma forma mais concisa, mas acho que não perderia muita coisa de performance:

const isLetter = char => /[a-z]/i.test(char)
const isNumber = char => /[0-9]/.test(char)
const isSpecial = char => /[!@#$%]/.test(char)

function filterChars(text, filters) {
  const result = text.split('').filter(char => {
    for (const filter of filters) {
      if (filter(char)) return true
    }

    return false
  })

  return result.join('')
}

const alphabet = 'abcde123456!@#$%'

console.log(filterChars(alphabet, [isLetter, isNumber])) // abcde123456
console.log(filterChars(alphabet, [isLetter, isSpecial])) // abcde!@#$%
console.log(filterChars(alphabet, [isNumber, isSpecial])) // 123456!@#$%

tipado:

type FilterFn = (char: string) => boolean

const isLetter: FilterFn = (char: string) => /[a-z]/i.test(char)
const isNumber: FilterFn = (char: string) => /[0-9]/.test(char)
const isSpecial: FilterFn = (char: string) => /[!@#$%]/.test(char)

function filterChars(text: string, filters: FilterFn[]) {
  const result = text.split('').filter(char => {
    for (const filter of filters) {
      if (filter(char)) return true
    }

    return false
  })

  return result.join('')
}

O que você acha?


Além disso, o nome não está bom. Eu entendi que se letter for true, vc não quer remover as letras, e sim mantê-las. Por isso o remove no início dos nomes dos filtros não me parece uma boa.

Aqui foi erro meu na hora de digitar, o certo seria:

.filter(removeLetters) // Filtro deve ser aplicado apenas se `options.letters` for `false`
2

Eu só não vejo a necessidade de se fazer o split (que transforma a string em um array), para depois usar o filter (que cria outro array) e por fim o join (que junta tudo em uma string). Acho uma volta muito grande, sendo que dá pra fazer apenas com um loop simples pelos caracteres.

Tem outra diferença importante, caso a string tenha caracteres "diferentões", como emojis, veja:

function filterComSplit(text, filters) {
    const result = text.split('').filter(char => {
            for (const filter of filters) {
                if (filter(char))
                    return true;
            }
            return false;
        });
    return result.join('');
}

function filterSemSplit(text, filters) {
    var result = '';
    for (const char of text) { // para cada caractere do texto
        // verifica se ele satisfaz algum filtro
        for (const filter of filters) {
            if (filter(char)) {
                result += char;
                break; // se já satisfaz um dos filtros, não preciso verificar os outros
            }
        }
    }
    return result;
}

// verifica se é um dos emojis: 💩 ou 😀
function isEmoji(char) {
    const codepoint = char.codePointAt(0);
    return codepoint == 0x1f4a9 || codepoint == 0x1f600;
}

// sim, pode colocar emoji direto na string (se o editor suportar, claro)
const alphabet = 'olá 💩 etc 😀!';

// usando "for..of" funciona
console.log(filterSemSplit(alphabet, [ isEmoji ])); // 💩😀

// usando split, não funciona
console.log(filterComSplit(alphabet, [ isEmoji ])); // string vazia (não imprime nada)

Se ficou curioso, tem uma explicação detalhada sobre este problema aqui. Claro que se a string só tiver texto "normal", isso não ocorre. Mas vale lembrar que há vários caracteres de outros idiomas que podem dar este mesmo problema.

Por fim, fiz alguns testes com o Benchmark.js e a versão com split ficou cerca de 40% mais lenta. Isso porque o filter recebe como parâmetro uma função de callback que é executada para cada um dos caracteres. Claro que para poucas strings pequenas, a diferença será imperceptível (o Benchmark.js roda milhões de casos para ter uma comparação melhor). Mas enfim, o problema nem era "deixar as funções mais concisas", e sim o fato de criar um array, filtrá-lo (criando outro array) e depois juntar tudo de novo.


Quanto a trocar function por arrow function, a questão nem é "ficar mais conciso". Tem que considerar as diferenças entre uma e outra (ver aqui). Tem casos que não faz diferença, mas tem casos que faz, e esse deveria ser o critério (ser "mais curto" é mero detalhe, e o menos importante neste caso).

2

Sobre os caracteres diferentes e emojis, pro meu caso de uso, não seria um problema já que os caracteres são conhecidos e definidos hardcoded ([a-z][A-Z][0-1][!@#$%&*]), mas entendi seu ponto, não sabia que o split() não pegava esses caracteres diferentes.

Também não cheguei a fazer um benchmark com .split() e .join(), fiz de cabeça mesmo kk, mas de novo, os caracteres usados são bem definidos e o tamanho desse alfabeto não vai ser grande a esse ponto. Vou refazer esse benchmark com meu caso de uso pra ver como fica.

Sobre as arrow functions eu não fazia a mínima ideia que tinha diferenças significativas além da sintaxe, vou da uma lida no post linkado. Obrigado!