How to check if a string “StartsWith” another string?

后端 未结 19 1327
我在风中等你
我在风中等你 2020-11-21 22:35

How would I write the equivalent of C#\'s String.StartsWith in JavaScript?

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

haystack.startsWith(needle)          


        
相关标签:
19条回答
  • 2020-11-21 22:54

    Also check out underscore.string.js. It comes with a bunch of useful string testing and manipulation methods, including a startsWith method. From the docs:

    startsWith _.startsWith(string, starts)

    This method checks whether string starts with starts.

    _("image.gif").startsWith("image")
    => true
    
    0 讨论(0)
  • 2020-11-21 22:54
    1. The question is a bit old, but I wanted to write this answer to show you some benchmarks I made based on all the answers provided here and the jsperf shared by Jim Buck.

    I basically needed a fast way to find if a long needle is within a long haystack and they are very similar except for the last characters.

    Here's the code I have written which for each function (splice, substring, startsWith, etc.) tests both when they return false and true against a haystack string (nestedString) of 1.000.0001 characters and a falsy or truthy needle string of 1.000.000 chars (testParentStringFalse and testParentStringTrue, respectively):

    // 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)
    }
    
    

    I runned this benchmark test on Chrome 75, Firefox 67, Safari 12 and Opera 62.

    I haven't included Edge and IE because I do not have them on this machine, but if someone of you wants to run the script against Edge and at least IE 9 and share the output here I would be very curious to see the results.

    Just remember that you need to recreate the 3 long strings and save the script in a file which you then open in your browser as copy/paste on the browser's console will block it as each string's length is >= 1.000.000).

    Here are the outputs:

    Chrome 75 (substring wins):

    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 wins):

    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 wins for false results, startsWith wins for true results, also Safari is the fastest in terms of total time to to execute the whole test):

    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 wins. Results are similar to Chrome and I am not surprised as Opera is based on Chromium and Blink):

    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}
    

    It turns out every browser has its own implementation details (apart Opera, which is based on Chrome's Chromium and Blink).

    Of course, further test with different use cases could and should be performed (e.g. when needle is really short compared to haystack, when haystack is shorter than needle, etc...), but in my case I needed to compare very long strings and wanted to share it here.

    0 讨论(0)
  • 2020-11-21 22:56

    If you are working with startsWith() and endsWith() then you have to be careful about leading spaces. Here is a complete example:

    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
    
    0 讨论(0)
  • 2020-11-21 23:01

    Another alternative with .lastIndexOf:

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

    This looks backwards through haystack for an occurrence of needle starting from index 0 of haystack. In other words, it only checks if haystack starts with needle.

    In principle, this should have performance advantages over some other approaches:

    • It doesn't search the entire haystack.
    • It doesn't create a new temporary string and then immediately discard it.
    0 讨论(0)
  • 2020-11-21 23:01

    Without a helper function, just using regex's .test method:

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

    To do this with a dynamic string rather than a hardcoded one (assuming that the string will not contain any regexp control characters):

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

    You should check out Is there a RegExp.escape function in Javascript? if the possibility exists that regexp control characters appear in the string.

    0 讨论(0)
  • 2020-11-21 23:01
    var str = 'hol';
    var data = 'hola mundo';
    if (data.length >= str.length && data.substring(0, str.length) == str)
        return true;
    else
        return false;
    
    0 讨论(0)
提交回复
热议问题