Pergunta

Como eu escreveria o equivalente a C#'s String.StartsWith em JavaScript?

var haystack = 'hello world';
var needle = 'he';

haystack.startsWith(needle) == true

Nota: Esta é uma pergunta antiga e, como apontado nos comentários, o Ecmascript 2015 (ES6) introduziu o .startsWith método. No entanto, no momento da redação desta atualização (2015) O suporte do navegador está longe de ser completo.

Foi útil?

Solução

Você pode usar o ecmascript 6's String.prototype.startsWith() método, mas é ainda não suportado em todos os navegadores. Você vai querer usar um Shim/Polyfill para adicioná -lo aos navegadores que não o suportam. Criando uma implementação que cumpra Todos os detalhes estabelecidos na especificação é um pouco complicado. Se você quer um calço fiel, use:

Depois de interromper o método (ou se você está suportando apenas navegadores e motores JavaScript que já o possuem), você pode usá -lo assim:

"Hello World!".startsWith("He"); // true

var haystack = "Hello world";
var prefix = 'orl';
haystack.startsWith(prefix); // false

Outras dicas

Outra alternativa com .lastIndexOf:

haystack.lastIndexOf(needle, 0) === 0

Isso olha para trás haystack para uma ocorrência de needle a partir do índice 0 do haystack. Em outras palavras, ele só verifica se haystack começa com needle.

Em princípio, isso deve ter vantagens de desempenho sobre algumas outras abordagens:

  • Não procura o inteiro haystack.
  • Ele não cria uma nova string temporária e a descarte imediatamente.
data.substring(0, input.length) === input

Sem uma função auxiliar, apenas usando o Regex's .test método:

/^He/.test('Hello world')

Para fazer isso com uma string dinâmica em vez de um codificado (supondo que a string não contenha nenhum caractere de controle regexp):

new RegExp('^' + needle).test(haystack)

Você deve conferir Existe uma função regexp.escape em JavaScript? Se existir a possibilidade de que os caracteres de controle regexp apareçam na string.

Melhor solução:

function startsWith(str, word) {
    return str.lastIndexOf(word, 0) === 0;
}

startsWith("aaa", "a")
true
startsWith("aaa", "ab")
false
startsWith("abc", "abc")
true
startsWith("abc", "c")
false
startsWith("abc", "a")
true
startsWith("abc", "ba")
false
startsWith("abc", "ab")
true

E aqui está termina com Se você precisar disso também:

function endsWith(str, word) {
    return str.indexOf(word, str.length - word.length) !== -1;
}

Para aqueles que preferem protótipo -o na string:

String.prototype.startsWith || (String.prototype.startsWith = function(word) {
    return this.lastIndexOf(word, 0) === 0;
});

String.prototype.endsWith   || (String.prototype.endsWith = function(word) {
    return this.indexOf(word, this.length - word.length) !== -1;
});

Uso:

"abc".startsWith("ab")
true
"c".ensdWith("c") 
true

Eu só queria adicionar minha opinião sobre isso.

Eu acho que podemos usar assim:

var haystack = 'hello world';
var needle = 'he';

if (haystack.indexOf(needle) == 0) {
  // Code if string starts with this substring
}

Aqui está uma pequena melhoria da solução do CMS:

if(!String.prototype.startsWith){
    String.prototype.startsWith = function (str) {
        return !this.indexOf(str);
    }
}

"Hello World!".startsWith("He"); // true

 var data = "Hello world";
 var input = 'He';
 data.startsWith(input); // true

Verificando se a função já existe caso um navegador futuro a implemente no código nativo ou se seja implementado por outra biblioteca. Por exemplo, a biblioteca do protótipo já implementa essa função.

Usando ! é um pouco mais rápido e mais conciso do que === 0 Embora não seja tão legível.

Verifique também subscore.string.js. Ele vem com um monte de métodos úteis de teste de string e manipulação, incluindo um startsWith método. Dos documentos:

começa com _.startsWith(string, starts)

Este método verifica se string começa com starts.

_("image.gif").startsWith("image")
=> true

Recentemente, me fiz a mesma pergunta.
Existem várias soluções possíveis, aqui estão três válidas:

  • s.indexOf(starter) === 0
  • s.substr(0,starter.length) === starter
  • s.lastIndexOf(starter, 0) === 0 (Adicionado depois de ver Mark Byers responda)
  • Usando um loop:

    function startsWith(s,starter) {
      for (var i = 0,cur_c; i < starter.length; i++) {
        cur_c = starter[i];
        if (s[i] !== starter[i]) {
          return false;
        }
      }
      return true;
    }
    

Não encontrei a última solução que utiliza um loop.
Surpreendentemente, essa solução supera os 3 primeiros por uma margem significativa.
Aqui está o teste JSPERF que realizei para chegar a esta conclusão: http://jsperf.com/startswith2/2

Paz

PS: Ecmascript 6 (Harmony) apresenta um nativo startsWith Método para cordas.
Pense em quanto tempo teria sido salvo se eles pensassem em incluir esse método tão necessário na própria versão inicial.

Atualizar

Como Steve apontou (o primeiro comentário sobre esta resposta), a função personalizada acima lançará um erro se o dado prefixo é mais curto que a corda inteira. Ele consertou isso e adicionou uma otimização de loop que pode ser vista em http://jsperf.com/startswith2/4.

Observe que existem 2 otimizações de loop que Steve incluiu, o primeiro dos dois mostrou melhor desempenho, portanto, postarei esse código abaixo:

function startsWith2(str, prefix) {
  if (str.length < prefix.length)
    return false;
  for (var i = prefix.length - 1; (i >= 0) && (str[i] === prefix[i]); --i)
    continue;
  return i < 0;
}

Como isso é tão popular, acho que vale a pena apontar que existe uma implementação para esse método na ECMA 6 e em preparação para isso deve usar o poli -preenchimento 'oficial' para evitar problemas e lágrimas futuras.

Felizmente, os especialistas da Mozilla nos fornecem um:

https://developer.mozilla.org/de/docs/web/javascript/reference/global_objects/string/startswith

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(searchString, position) {
        position = position || 0;
        return this.indexOf(searchString, position) === position;
    };
}

Observe que isso tem a vantagem de ser graciosamente ignorado na transição para a ECMA 6.

A melhor solução de desempenho é parar de usar chamadas de biblioteca e apenas reconhecer que você está trabalhando com duas matrizes. Uma implementação enrolada à mão é curta e também mais rápida que todas as outras soluções que eu já vi aqui.

function startsWith2(str, prefix) {
    if (str.length < prefix.length)
        return false;
    for (var i = prefix.length - 1; (i >= 0) && (str[i] === prefix[i]); --i)
        continue;
    return i < 0;
}

Para comparações de desempenho (sucesso e fracasso), veja http://jsperf.com/startswith2/4. (Certifique -se de verificar versões posteriores que podem ter superado as minhas.)

Acabei de aprender sobre esta biblioteca de string:

http://stringjs.com/

Inclua o arquivo JS e depois use o S variável como esta:

S('hi there').endsWith('hi there')

Também pode ser usado no NodeJS instalando -o:

npm install string

Então exigindo isso como o S variável:

var S = require('string');

A página da web também possui links para bibliotecas de string alternativas, se essa não gosta.

var str = 'hol';
var data = 'hola mundo';
if (data.length >= str.length && data.substring(0, str.length) == str)
    return true;
else
    return false;

Com base nas respostas aqui, esta é a versão que estou usando agora, pois parece fornecer o melhor desempenho com base nos testes JSPERF (e está funcionalmente completo até onde eu sei).

if(typeof String.prototype.startsWith != 'function'){
    String.prototype.startsWith = function(str){
        if(str == null) return false;
        var i = str.length;
        if(this.length < i) return false;
        for(--i; (i >= 0) && (this[i] === str[i]); --i) continue;
        return i < 0;
    }
}

Isso foi baseado no startSwith2 daqui: http://jsperf.com/startswith2/6. Adicionei um pequeno ajuste para uma pequena melhoria de desempenho e, desde então, também adicionei uma verificação para a string de comparação nula ou indefinida e a converti para adicionar ao protótipo da string usando a técnica na resposta do CMS.

Observe que esta implementação não suporta o parâmetro "posição" mencionado neste Rede de desenvolvedores Mozilla Página, mas isso não parece fazer parte da proposta do ECMAScript de qualquer maneira.

Não tenho certeza para o JavaScript, mas no TypeScript eu fiz algo como

var str = "something";
(<String>str).startsWith("some");

Eu acho que deve funcionar no JS também. Espero que ajude!

  1. A pergunta é um pouco antiga, mas eu queria escrever esta resposta para mostrar alguns benchmarks que fiz com base em todas as respostas fornecidas aqui e no JSPERF compartilhado por Jim Buck.

Eu basicamente precisava de uma maneira rápida de descobrir se uma agulha longa está dentro de um palheiro longo e eles são muito semelhantes, exceto pelos últimos caracteres.

Aqui está o código que escrevi que para cada função (emenda, substring, startSwith etc.) testes quando eles retornam falsos e verdadeiros contra uma string haystack (nestedString) de 1.000.0001 caracteres e uma corda de agulha falsamente ou verdadeira de 1.000.000 chars (testParentStringFalse e testParentStringTrue, respectivamente):

// nestedString is made of 1.000.001 '1' repeated characters.
var nestedString = '...'

// testParentStringFalse is made of 1.000.000 characters,
// all characters are repeated '1', but the last one is '2',
// so for this string the test should return false.
var testParentStringFalse = '...'

// testParentStringTrue is made of 1.000.000 '1' repeated characters,
// so for this string the test should return true.
var testParentStringTrue = '...'

// You can make these very long strings by running the following bash command
// and edit each one as needed in your editor
// (NOTE: on OS X, `pbcopy` copies the string to the clipboard buffer,
//        on Linux, you would probably need to replace it with `xclip`):
// 
//     printf '1%.0s' {1..1000000} | pbcopy
// 

function testString() {
    let dateStart
    let dateEnd
    let avg
    let count = 100000
    const falseResults = []
    const trueResults = []

    /* slice */
    console.log('========> slice')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.slice(0, testParentStringFalse.length) === testParentStringFalse
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'slice',
        avg
    }
    console.log(`testString() slice = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.slice(0, testParentStringTrue.length) === testParentStringTrue
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'slice',
        avg
    }
    console.log(`testString() slice = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== slice')
    console.log('')
    /* slice END */

    /* lastIndexOf */
    console.log('========> lastIndexOf')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.lastIndexOf(testParentStringFalse, 0) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'lastIndexOf',
        avg
    }
    console.log(`testString() lastIndexOf = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.lastIndexOf(testParentStringTrue, 0) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'lastIndexOf',
        avg
    }
    console.log(`testString() lastIndexOf = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== lastIndexOf')
    console.log('')
    /* lastIndexOf END */

    /* indexOf */
    console.log('========> indexOf')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.indexOf(testParentStringFalse) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'indexOf',
        avg
    }
    console.log(`testString() indexOf = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.indexOf(testParentStringTrue) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'indexOf',
        avg
    }
    console.log(`testString() indexOf = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== indexOf')
    console.log('')
    /* indexOf END */

    /* substring */
    console.log('========> substring')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.substring(0, testParentStringFalse.length) === testParentStringFalse
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'substring',
        avg
    }
    console.log(`testString() substring = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.substring(0, testParentStringTrue.length) === testParentStringTrue
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'substring',
        avg
    }
    console.log(`testString() substring = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== substring')
    console.log('')
    /* substring END */

    /* startsWith */
    console.log('========> startsWith')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.startsWith(testParentStringFalse)
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'startsWith',
        avg
    }
    console.log(`testString() startsWith = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.startsWith(testParentStringTrue)
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'startsWith',
        avg
    }
    console.log(`testString() startsWith = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== startsWith')
    console.log('')
    /* startsWith END */

    falseResults.sort((a, b) => a.avg - b.avg)
    trueResults.sort((a, b) => a.avg - b.avg)

    console.log('false results from fastest to slowest avg:', falseResults)
    console.log('true results from fastest to slowest avg:', trueResults)
}

Eu executei este teste de referência em Chrome 75, Firefox 67, Safari 12 e Opera 62.

Não incluí o Edge e o IE porque não os tenho nesta máquina, mas se alguém quiser executar o script contra o Edge e pelo menos IE 9 e compartilhar a saída aqui, ficaria muito curioso para ver os resultados.

Lembre -se de que você precisa recriar as três cordas longas e salvar o script em um arquivo que você abre no seu navegador como copiar/colar no console do navegador o bloqueará, pois o comprimento de cada sequência é> = 1.000.000).

Aqui estão as saídas:

Chrome 75 (substring vitórias):

false results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08271}
2)  {"label":"slice","avg":0.08615}
3)  {"label":"lastIndexOf","avg":0.77025}
4)  {"label":"indexOf","avg":1.64375}
5)  {"label":"startsWith","avg":3.5454}

true results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08213}
2)  {"label":"slice","avg":0.08342}
3)  {"label":"lastIndexOf","avg":0.7831}
4)  {"label":"indexOf","avg":0.88988}
5)  {"label":"startsWith","avg":3.55448}

Firefox 67 (indexOf vitórias):

false results from fastest to slowest avg
1)  {"label":"indexOf","avg":0.1807}
2)  {"label":"startsWith","avg":0.74621}
3)  {"label":"substring","avg":0.74898}
4)  {"label":"slice","avg":0.78584}
5)  {"label":"lastIndexOf","avg":0.79668}

true results from fastest to slowest avg:
1)  {"label":"indexOf","avg":0.09528}
2)  {"label":"substring","avg":0.75468}
3)  {"label":"startsWith","avg":0.76717}
4)  {"label":"slice","avg":0.77222}
5)  {"label":"lastIndexOf","avg":0.80527}

Safari 12 (slice vence por resultados falsos, startsWith Vitórias para resultados verdadeiros, também o Safari é o mais rápido em termos de tempo total para executar todo o teste):

false results from fastest to slowest avg:
1) "{\"label\":\"slice\",\"avg\":0.0362}"
2) "{\"label\":\"startsWith\",\"avg\":0.1141}"
3) "{\"label\":\"lastIndexOf\",\"avg\":0.11512}"
4) "{\"label\":\"substring\",\"avg\":0.14751}"
5) "{\"label\":\"indexOf\",\"avg\":0.23109}"

true results from fastest to slowest avg:
1) "{\"label\":\"startsWith\",\"avg\":0.11207}"
2) "{\"label\":\"lastIndexOf\",\"avg\":0.12196}"
3) "{\"label\":\"substring\",\"avg\":0.12495}"
4) "{\"label\":\"indexOf\",\"avg\":0.33667}"
5) "{\"label\":\"slice\",\"avg\":0.49923}"

Opera 62 (substring vence. Os resultados são semelhantes ao Chrome e não estou surpreso, pois a ópera é baseada em cromo e pisca):

false results from fastest to slowest avg:
{"label":"substring","avg":0.09321}
{"label":"slice","avg":0.09463}
{"label":"lastIndexOf","avg":0.95347}
{"label":"indexOf","avg":1.6337}
{"label":"startsWith","avg":3.61454}

true results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08855}
2)  {"label":"slice","avg":0.12227}
3)  {"label":"indexOf","avg":0.79914}
4)  {"label":"lastIndexOf","avg":1.05086}
5)  {"label":"startsWith","avg":3.70808}

Acontece que todos os navegadores têm seus próprios detalhes de implementação (Opera Apart, que é baseado no Chrome e no Chrome e Blink).

Obviamente, testes adicionais com diferentes casos de uso podem e devem ser realizados (por exemplo, quando a agulha é realmente curta em comparação com o palheiro, quando o palheiro é mais curto que a agulha, etc ...), mas no meu caso eu precisava comparar cordas muito longas e queria compartilhar aqui.

Se você está trabalhando com startsWith() e endsWith() Então você deve ter cuidado com os principais espaços. Aqui está um exemplo completo:

var str1 = " Your String Value Here.!! "; // Starts & ends with spaces    
if (str1.startsWith("Your")) { }  // returns FALSE due to the leading spaces…
if (str1.endsWith("Here.!!")) { } // returns FALSE due to trailing spaces…

var str2 = str1.trim(); // Removes all spaces (and other white-space) from start and end of `str1`.
if (str2.startsWith("Your")) { }  // returns TRUE
if (str2.endsWith("Here.!!")) { } // returns TRUE

Você também pode devolver todos os membros de uma matriz que começam com uma string criando seu próprio protótipo / extensão ao protótipo da matriz, também conhecido como

Array.prototype.mySearch = function (target) {
    if (typeof String.prototype.startsWith != 'function') {
        String.prototype.startsWith = function (str){
        return this.slice(0, str.length) == str;
      };
    }
    var retValues = [];
    for (var i = 0; i < this.length; i++) {
        if (this[i].startsWith(target)) { retValues.push(this[i]); }
    }
    return retValues;
};

E para usá -lo:

var myArray = ['Hello', 'Helium', 'Hideout', 'Hamster'];
var myResult = myArray.mySearch('Hel');
// result -> Hello, Helium
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top