-
-
Save FeMaffezzolli/408d8634b4ec0919787e0837b9fadbc6 to your computer and use it in GitHub Desktop.
const isValidCpf = (cpfValue) => { | |
if (!cpfValue) return false | |
const formatToString = val => { | |
if (Array.isArray(val)) return val.join('') | |
if (typeof val === 'string') return val | |
return null | |
} | |
const cpfString = formatToString(cpfValue) | |
if (!cpfString) return false | |
let cpf = cpfString.replace(/[^\d]+/g, '') | |
// Validates length | |
if (cpf.length !== 11) return false | |
// Clear invalids | |
if (/^(\d)\1+$/.test(cpf)) return false | |
let sum = 0 | |
let remain | |
for (let i = 1; i <= 9; i++) | |
sum = sum + parseInt(cpf.substring(i - 1, i)) * (11 - i) | |
remain = (sum * 10) % 11 | |
if (remain == 10 || remain == 11) remain = 0 | |
if (remain != parseInt(cpf.substring(9, 10))) return false | |
sum = 0 | |
for (let i = 1; i <= 10; i++) | |
sum = sum + parseInt(cpf.substring(i - 1, i)) * (12 - i) | |
remain = (sum * 10) % 11 | |
if (remain == 10 || remain == 11) remain = 0 | |
if (remain != parseInt(cpf.substring(10, 11))) return false | |
return true | |
} |
Opa, legal cara, gostei da versão simples e concisa! É uma forma de implementar o algoritmo e funcionando para o seu caso está ótimo.
Já tive uma versão semelhante, mas acabei evoluindo até aquela que você viu por alguns pontos que descobri serem importantes no meu desenvolvimento e nos casos de uso que encarei.
Como você me pediu comentando na minha Gist cpf.validation.js, seguem alguns pontos abaixo. Espero que ajudem como uma crítica construtiva:
O que acontece se o método recebe um valor inválido? Deveria retornar
false
, pois não é um CPF válido.
- Ex.:
isValidCpf()
,isValidCpf(0)
,isValidCpf(null)
,isValidCpf(undefined)
,isValidCpf(AnyThing)
, ... No caso do seu método, quebraria o fluxo da aplicação em um erro JS.Posso receber o valor de CPF como número inteiro ou string, formatada ou não.
- Ex.:
isValidCpf(81850453721)
,isValidCpf('81850453721')
,isValidCpf('818.504.537-21')
,isValidCpf('818.504.537.21')
Reutilizo funções de cálculo, ao invés de escrever os cálculos novamente para cada verificação. E ao invés de repetir várias vezes números que são constantes e têm base na quantidade de dígitos incluídos no cálculo, capturo diretamente do tamanho do próprio bloco. Isso torna a lógica mais compreensível e menos arbitrária.
- Ex.: Confira seu código a partir da linha 12. Há 2 laços
for
que fazem a mesma coisa apenas com parâmetros diferentes. Veja quantas vezes você repete os números 9, 10 e 11 e faz isso parecer totalmente arbitrário no código, quando na verdade eles tem base na quantidade de dígitos incluídos no cálculo a cada verificação.Além disso:
Meu código tem mais quebras de linha e comentários explicativos, visando maior clareza na leitura, o que aumenta muito a quantidade de linhas.
Uma condição muito incomum, mas que foi necessária nas aplicações que fiz, foi a possibilidade de o valor do CPF entrar como um Array de dígitos, sendo números ou strings.
- Ex.:
isValidCpf([8, 1, 8, 5, 0, 4, 5, 3, 7, 2, 1])
,isValidCpf(['8', '1', '8', '5', '0', '4', '5', '3', '7', '2', '1'])
Fala Alex, muito obrigado pelo seu tempo e atenção.
De fato tinham algumas coisas básicas ali para corrigir (como tratar undefined, null e vazio). Fiz isso de imediato.
E implementei também a possibilidade de receber um array, assim como no seu caso.
Após isso, eu fiz um script de test com alguns cenários e medindo o tempo. A sua implementação ficou incrivelmente mais rápida, quase 12 vezes mais rápida.
Obs.: Result true indica sucesso na validação do CPF.
Run test number 0 - Result: true - Payload: [40010845895]
Run test number 1 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Run test number 2 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Run test number 3 - Result: true - Payload: [794.752.070-40]
Run test number 4 - Result: true - Payload: [266.422.050-06]
Run test number 5 - Result: true - Payload: [40010845896]
Run test number 6 - Result: true - Payload: [40010845896]
Run test number 7 - Result: true - Payload: [null]
Run test number 8 - Result: true - Payload: [undefined]
Run test number 9 - Result: true - Payload: [[object Object]]
First: 8.096ms
Run test number 0 - Result: true - Payload: [40010845895]
Run test number 1 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Run test number 2 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Run test number 3 - Result: true - Payload: [794.752.070-40]
Run test number 4 - Result: true - Payload: [266.422.050-06]
Run test number 5 - Result: true - Payload: [40010845896]
Run test number 6 - Result: true - Payload: [40010845896]
Run test number 7 - Result: true - Payload: [null]
Run test number 8 - Result: true - Payload: [undefined]
Run test number 9 - Result: true - Payload: [[object Object]]
Second: 0.698ms
E eu achei isso extremamente interessante. Por que embora no seu código não tenha nenhum laço explícito, as funções 'map' e 'reduce' não deixam de ser um laço, e você utiliza 5 vezes (versus dois laços no meu). Eu vou um pouco mais fundo para entender o motivo dessa discrepância.
Abs.
Opa, legal cara, gostei da versão simples e concisa! É uma forma de implementar o algoritmo e funcionando para o seu caso está ótimo.
Já tive uma versão semelhante, mas acabei evoluindo até aquela que você viu por alguns pontos que descobri serem importantes no meu desenvolvimento e nos casos de uso que encarei.
Como você me pediu comentando na minha Gist cpf.validation.js, seguem alguns pontos abaixo. Espero que ajudem como uma crítica construtiva:
O que acontece se o método recebe um valor inválido? Deveria retornar
false
, pois não é um CPF válido.
- Ex.:
isValidCpf()
,isValidCpf(0)
,isValidCpf(null)
,isValidCpf(undefined)
,isValidCpf(AnyThing)
, ... No caso do seu método, quebraria o fluxo da aplicação em um erro JS.Posso receber o valor de CPF como número inteiro ou string, formatada ou não.
- Ex.:
isValidCpf(81850453721)
,isValidCpf('81850453721')
,isValidCpf('818.504.537-21')
,isValidCpf('818.504.537.21')
Reutilizo funções de cálculo, ao invés de escrever os cálculos novamente para cada verificação. E ao invés de repetir várias vezes números que são constantes e têm base na quantidade de dígitos incluídos no cálculo, capturo diretamente do tamanho do próprio bloco. Isso torna a lógica mais compreensível e menos arbitrária.
- Ex.: Confira seu código a partir da linha 12. Há 2 laços
for
que fazem a mesma coisa apenas com parâmetros diferentes. Veja quantas vezes você repete os números 9, 10 e 11 e faz isso parecer totalmente arbitrário no código, quando na verdade eles tem base na quantidade de dígitos incluídos no cálculo a cada verificação.Além disso:
Meu código tem mais quebras de linha e comentários explicativos, visando maior clareza na leitura, o que aumenta muito a quantidade de linhas.
Uma condição muito incomum, mas que foi necessária nas aplicações que fiz, foi a possibilidade de o valor do CPF entrar como um Array de dígitos, sendo números ou strings.
- Ex.:
isValidCpf([8, 1, 8, 5, 0, 4, 5, 3, 7, 2, 1])
,isValidCpf(['8', '1', '8', '5', '0', '4', '5', '3', '7', '2', '1'])
Fala Alex, muito obrigado pelo seu tempo e atenção.
De fato tinham algumas coisas básicas ali para corrigir (como tratar undefined, null e vazio). Fiz isso de imediato.
E implementei também a possibilidade de receber um array, assim como no seu caso.Após isso, eu fiz um script de test com alguns cenários e medindo o tempo. A sua implementação ficou incrivelmente mais rápida, quase 12 vezes mais rápida.
Obs.: Result true indica sucesso na validação do CPF.
Run test number 0 - Result: true - Payload: [40010845895] Run test number 1 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5] Run test number 2 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5] Run test number 3 - Result: true - Payload: [794.752.070-40] Run test number 4 - Result: true - Payload: [266.422.050-06] Run test number 5 - Result: true - Payload: [40010845896] Run test number 6 - Result: true - Payload: [40010845896] Run test number 7 - Result: true - Payload: [null] Run test number 8 - Result: true - Payload: [undefined] Run test number 9 - Result: true - Payload: [[object Object]] First: 8.096ms Run test number 0 - Result: true - Payload: [40010845895] Run test number 1 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5] Run test number 2 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5] Run test number 3 - Result: true - Payload: [794.752.070-40] Run test number 4 - Result: true - Payload: [266.422.050-06] Run test number 5 - Result: true - Payload: [40010845896] Run test number 6 - Result: true - Payload: [40010845896] Run test number 7 - Result: true - Payload: [null] Run test number 8 - Result: true - Payload: [undefined] Run test number 9 - Result: true - Payload: [[object Object]] Second: 0.698msE eu achei isso extremamente interessante. Por que embora no seu código não tenha nenhum laço explícito, as funções 'map' e 'reduce' não deixam de ser um laço, e você utiliza 5 vezes (versus dois laços no meu). Eu vou um pouco mais fundo para entender o motivo dessa discrepância.
Abs.
Na verdade acho que eu entendi (e talvez seja alguma coisa relacionada ao node em si). Eu simplesmente troquei a ordem de execução, rodei primeiro seu código e então o meu. E adivinha o que aconteceu: o seu passou e levar os mesmos 8 ms (que antes o meu levava) e o meu 0.8ms (similar ao seu).
Run test number 0 - Result: true - Payload: [40010845895]
Alex"s 0 run: 6.939ms
Run test number 1 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Alex"s 1 run: 0.086ms
Run test number 2 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Alex"s 2 run: 0.057ms
Run test number 3 - Result: true - Payload: [794.752.070-40]
Alex"s 3 run: 0.060ms
Run test number 4 - Result: true - Payload: [266.422.050-06]
Alex"s 4 run: 0.073ms
Run test number 5 - Result: true - Payload: [40010845896]
Alex"s 5 run: 0.052ms
Run test number 6 - Result: true - Payload: [40010845896]
Alex"s 6 run: 0.068ms
Run test number 7 - Result: true - Payload: [null]
Alex"s 7 run: 0.030ms
Run test number 8 - Result: true - Payload: [undefined]
Alex"s 8 run: 0.042ms
Run test number 9 - Result: true - Payload: [[object Object]]
Alex"s 9 run: 0.028ms
Alex"s total: 7.955ms
Run test number 0 - Result: true - Payload: [40010845895]
Felipe"s 0 run: 0.298ms
Run test number 1 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Felipe"s 1 run: 0.047ms
Run test number 2 - Result: true - Payload: [4,0,0,1,0,8,4,5,8,9,5]
Felipe"s 2 run: 0.034ms
Run test number 3 - Result: true - Payload: [794.752.070-40]
Felipe"s 3 run: 0.035ms
Run test number 4 - Result: true - Payload: [266.422.050-06]
Felipe"s 4 run: 0.024ms
Run test number 5 - Result: true - Payload: [40010845896]
Felipe"s 5 run: 0.027ms
Run test number 6 - Result: true - Payload: [40010845896]
Felipe"s 6 run: 0.024ms
Run test number 7 - Result: true - Payload: [null]
Felipe"s 7 run: 0.021ms
Run test number 8 - Result: true - Payload: [undefined]
Felipe"s 8 run: 0.019ms
Run test number 9 - Result: true - Payload: [[object Object]]
Felipe"s 9 run: 0.025ms
Felipe"s total: 0.900ms
Estou falando por achismo, mas me parece que o processo de inicialização do laço (ou do próprio console.time) leva uns 7 ms, e acaba inflando o primeiro contador.
E na análise de performance individual, os tempos ficaram muito próximos.
Abraço!
Fala cara, como vai?
Interessante! Eu não saberia dizer de fato o que pode estar causando isso.
Não sei como você testou, mas imagino que talvez uma suite de testes usando Jest ou Mocha e rodando os testes de cada método em arquivos de testes separados possa ser mais preciso.
Essas ferramentas de teste já mostram o tempo de execução no resultado no console.
Acredito que o seu script possa performar um pouco melhor que o meu, mas a diferença deve ser mínima.
Opa, legal cara, gostei da versão simples e concisa! É uma forma de implementar o algoritmo e funcionando para o seu caso está ótimo.
Já tive uma versão semelhante, mas acabei evoluindo até aquela que você viu por alguns pontos que descobri serem importantes no meu desenvolvimento e nos casos de uso que encarei.
Como você me pediu comentando na minha Gist cpf.validation.js, seguem alguns pontos abaixo. Espero que ajudem como uma crítica construtiva:
false
, pois não é um CPF válido.isValidCpf()
,isValidCpf(0)
,isValidCpf(null)
,isValidCpf(undefined)
,isValidCpf(AnyThing)
, ... No caso do seu método, quebraria o fluxo da aplicação em um erro JS.isValidCpf(81850453721)
,isValidCpf('81850453721')
,isValidCpf('818.504.537-21')
,isValidCpf('818.504.537.21')
for
que fazem a mesma coisa apenas com parâmetros diferentes. Veja quantas vezes você repete os números 9, 10 e 11 e faz isso parecer totalmente arbitrário no código, quando na verdade eles tem base na quantidade de dígitos incluídos no cálculo a cada verificação.Além disso:
isValidCpf([8, 1, 8, 5, 0, 4, 5, 3, 7, 2, 1])
,isValidCpf(['8', '1', '8', '5', '0', '4', '5', '3', '7', '2', '1'])