Flagrant Badassery

A JavaScript and regular expression centric blog

Faster JavaScript Trim

Since JavaScript doesn't include a trim method natively, it's included by countless JavaScript libraries – usually as a global function or appended to String.prototype. However, I've never seen an implementation which performs as well as it could, probably because most programmers don't deeply understand or care about regex efficiency issues.

After seeing a particularly bad trim implementation, I decided to do a little research towards finding the most efficient approach. Before getting into the analysis, here are the results:

Method Firefox 2 IE 6
trim1 15ms < 0.5ms
trim2 31ms < 0.5ms
trim3 46ms 31ms
trim4 47ms 46ms
trim5 156ms 1656ms
trim6 172ms 2406ms
trim7 172ms 1640ms
trim8 281ms < 0.5ms
trim9 125ms 78ms
trim10 < 0.5ms < 0.5ms
trim11 < 0.5ms < 0.5ms

Note 1: The comparison is based on trimming the Magna Carta (over 27,600 characters) with a bit of leading and trailing whitespace 20 times on my personal system. However, the data you're trimming can have a major impact on performance, which is detailed below.

Note 2: trim4 and trim6 are the most commonly found in JavaScript libraries today.

Note 3: The aforementioned bad implementation is not included in the comparison, but is shown later.

The analysis

Although there are 11 rows in the table above, they are only the most notable (for various reasons) of about 20 versions I wrote and benchmarked against various types of strings. The following analysis is based on testing in Firefox 2.0.0.4, although I have noted where there are major differences in IE6.

  1. return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    All things considered, this is probably the best all-around approach. Its speed advantage is most notable with long strings — when efficiency matters. The speed is largely due to a number of optimizations internal to JavaScript regex interpreters which the two discrete regexes here trigger. Specifically, the pre-check of required character and start of string anchor optimizations, possibly among others.
  2. return str.replace(/^\s+/, '').replace(/\s+$/, '');
    Very similar to trim1 (above), but a little slower since it doesn't trigger all of the same optimizations.
  3. return str.substring(Math.max(str.search(/\S/), 0), str.search(/\S\s*$/) + 1);
    This is often faster than the following methods, but slower than the above two. Its speed comes from its use of simple, character-index lookups.
  4. return str.replace(/^\s+|\s+$/g, '');
    This commonly thought up approach is easily the most frequently used in JavaScript libraries today. It is generally the fastest implementation of the bunch only when working with short strings which don't include leading or trailing whitespace. This minor advantage is due in part to the initial-character discrimination optimization it triggers. While this is a relatively decent performer, it's slower than the three methods above when working with longer strings, because the top-level alternation prevents a number of optimizations which could otherwise kick in.
  5. str = str.match(/\S+(?:\s+\S+)*/);
    return str ? str[0] : '';

    This is generally the fastest method when working with empty or whitespace-only strings, due to the pre-check of required character optimization it triggers. Note: In IE6, this can be quite slow when working with longer strings.
  6. return str.replace(/^\s*(\S*(\s+\S+)*)\s*$/, '$1');
    This is a relatively common approach, popularized in part by some leading JavaScripters. It's similar in approach (but inferior) to trim8. There's no good reason to use this in JavaScript, especially since it can be very slow in IE6.
  7. return str.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, '$1');
    The same as trim6, but a bit faster due to the use of a non-capturing group (which doesn't work in IE 5.0 and lower). Again, this can be slow in IE6.
  8. return str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
    This uses a simple, single-pass, greedy approach. In IE6, this is crazy fast! The performance difference indicates that IE has superior optimization for quantification of "any character" tokens.
  9. return str.replace(/^\s*([\S\s]*?)\s*$/, '$1');
    This is generally the fastest with very short strings which contain both non-space characters and edge whitespace. This minor advantage is due to the simple, single-pass, lazy approach it uses. Like trim8, this is significantly faster in IE6 than Firefox 2.

Since I've seen the following additional implementation in one library, I'll include it here as a warning:

return str.replace(/^\s*([\S\s]*)\b\s*$/, '$1');

Although the above is sometimes the fastest method when working with short strings which contain both non-space characters and edge whitespace, it performs very poorly with long strings which contain numerous word boundaries, and it's terrible (!) with long strings comprised of nothing but whitespace, since that triggers an exponentially increasing amount of backtracking. Do not use.

A different endgame

There are two methods in the table at the top of this post which haven't been covered yet. For those, I've used a non-regex and hybrid approach.

After comparing and analyzing all of the above, I wondered how an implementation which used no regular expressions would perform. Here's what I tried:

function trim10 (str) {
	var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
	for (var i = 0; i < str.length; i++) {
		if (whitespace.indexOf(str.charAt(i)) === -1) {
			str = str.substring(i);
			break;
		}
	}
	for (i = str.length - 1; i >= 0; i--) {
		if (whitespace.indexOf(str.charAt(i)) === -1) {
			str = str.substring(0, i + 1);
			break;
		}
	}
	return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
}

How does that perform? Well, with long strings which do not contain excessive leading or trailing whitespace, it blows away the competition (except against trim1/2/8 in IE, which are already insanely fast there).

Does that mean regular expressions are slow in Firefox? No, not at all. The issue here is that although regexes are very well suited for trimming leading whitespace, apart from the .NET library (which offers a somewhat-mysterious "backwards matching" mode), they don't really provide a method to jump to the end of a string without even considering previous characters. However, the non-regex-reliant trim10 function does just that, with the second loop working backwards from the end of the string until it finds a non-whitespace character.

Knowing that, what if we created a hybrid implementation which combined a regex's universal efficiency at trimming leading whitespace with the alternative method's speed at removing trailing characters?

function trim11 (str) {
	str = str.replace(/^\s+/, '');
	for (var i = str.length - 1; i >= 0; i--) {
		if (/\S/.test(str.charAt(i))) {
			str = str.substring(0, i + 1);
			break;
		}
	}
	return str;
}

Although the above is a bit slower than trim10 with some strings, it uses significantly less code and is still lightning fast. Plus, with strings which contain a lot of leading whitespace (which includes strings comprised of nothing but whitespace), it's much faster than trim10.

In conclusion…

Since the differences between the implementations cross-browser and when used with different data are both complex and nuanced (none of them are faster than all the others with any data you can throw at it), here are my general recommendations for a trim method:

  • Use trim1 if you want a general-purpose implementation which is fast cross-browser.
  • Use trim11 if you want to handle long strings exceptionally fast in all browsers.

To test all of the above implementations for yourself, try my very rudimentary benchmarking page. Background processing can cause the results to be severely skewed, so run the test a number of times (regardless of how many iterations you specify) and only consider the fastest results (since averaging the cost of background interference is not very enlightening).

As a final note, although some people like to cache regular expressions (e.g. using global variables) so they can be used repeatedly without recompilation, IMO this does not make much sense for a trim method. All of the above regexes are so simple that they typically take no more than a nanosecond to compile. Additionally, some browsers automatically cache the most recently used regexes, so a typical loop which uses trim and doesn't contain a bunch of other regexes might not encounter recompilation anyway.


Edit (2008-02-04): Shortly after posting this I realized trim10/11 could be better written. Several people have also posted improved versions in the comments. Here's what I use now, which takes the trim11-style hybrid approach:

function trim12 (str) {
	var	str = str.replace(/^\s\s*/, ''),
		ws = /\s/,
		i = str.length;
	while (ws.test(str.charAt(--i)));
	return str.slice(0, i + 1);
}

New library: Are you a JavaScript regex master, or want to be? Then you need my fancy XRegExp library. It adds new regex syntax (including named capture and Unicode properties); s, x, and n flags; powerful regex utils; and it fixes pesky browser inconsistencies. Check it out!

There Are 207 Responses So Far. »

  1. […] Another good post for : Faster JavaScript Trim […]

  2. […] Another good post for : Faster JavaScript Trim […]

  3. […] details on trim function and benchmarking different trim functions can be found here This entry was posted in JavaScript and tagged How To?, Internet explorer, Javascript, Trim. […]

  4. Using Node JS (chrome browser’s v8 engine)

    The best trim function is trim 11.

    Regular expression 4 took about the same time for both the magna carta, and a whitespace-only string, and took O(n) time.

    Trim 11 took the same time as the regular expression when set with the worst case scenario of handling whitespace-only strings. When set with the magna carta, trim 11 was shockingly faster.

    Units are in ms/1000tests. Lower is better.

    Long Whitespace Only
    [Trim Regexp, Trim 11]
    [ 114, 116 ], // 25,000 characters
    [ 69, 68 ], // 15,000 characters
    [ 46, 45 ],
    [ 23, 23 ],
    [ 11, 12 ],
    [ 5, 4 ],
    [ 3, 2 ],
    [ 1, 2 ],
    [ 0, 1 ],
    [ 0, 0 ],
    [ 1, 0 ],
    [ 0, 0 ] ] // 5 characters
    Short Whitespace Only

    Long Magna Carta
    [Trim Regexp, Trim 11]
    [ 187, 8 ],// 25,000 characters
    [ 113, 5 ],// 15,000 characters
    [ 75, 3 ],
    [ 38, 1 ],
    [ 19, 1 ],
    [ 7, 1 ],
    [ 4, 1 ],
    [ 2, 0 ],
    [ 1, 0 ],
    [ 1, 0 ],
    [ 1, 0 ],
    [ 1, 0 ], // 5 characters
    Short Magna Carta

  5. NodeJs users:

    Trim 11 and trim 12 are transformed into functionally identical code in V8. No performance advantage is gained by using one over the other. I use the following variation, as it is the most clear way to represent the code.

    function trim (str) {
    // Trim left
    var str = str.replace(/^\s\s*/, ”);

    // Trim right
    for (i= str.length; i–;)
    {
    if (/\S/.test(str[i]))
    {
    return str.substring(0, i + 1);
    }
    }
    return str
    }

  6. Im not sure, but why are you guys always checking an array of possible whitespace chars? Isnt that very slow? If think the fastest approach is to check the unicode values. you can do it with 3 checks.

    1. Smaller than 33 -> space
    2. Smaller than 256 -> non-space
    3. If bigger than smallest space character and smaller than the biggest-> Check other UnicodeRanges (but in range checks)

    Depending on the expeceted data set (especially the expected space chars), the order could be different.

  7. […] ???http://blog.stevenlevithan.com/archives/faster-trim-javascripthttp://www.iteye.com/topic/1021677 ?????, ??, ??, ??, ??, ??, ??, ???, ???, ?? ← javascript ???????? javascript???trim()??(???????????) → ?????0 ???? […]

  8. […] little reading (still not believing that IE8 didn’t have trim for strings) I found this post http://blog.stevenlevithan.com/archives/faster-trim-javascript. As it turns out, the default trim is kind of slow. Though I generally avoid overriding […]

  9. Maybe the fastest trim so far is found. Please check test results:
    http://jsperf.com/mega-trim-test/7

    Added new trim27, which beats CLEARLY all 24 trim-functions but the native trim. And beats also native trim of Chrome and Chromium. I suggest to use trim27 in cases where native trim is not available. It seems that old-school something || something is stunningly fast.

    Of course if we change the test data, the results may vary.

    Here it is:

    function trim27(str) {
        var c;
        for (var i = 0; i < str.length; i++) {
            c = str.charCodeAt(i);
            if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12) continue; else break;
        }
        for (var j = str.length - 1; j >= i; j--) {
            c = str.charCodeAt(j);
            if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12) continue; else break;
        }
        return str.substring(i, j + 1);
    }
  10. @Timo, thanks the new version and for putting up the whole collection on jsPerf!

  11. I replaced function trim1() in http://stevenlevithan.com/demo/trim.cfm using Opera 12.02 with the following function. Run 20 000 times and realized that the time was 8 ms. The next speediest was trim10: 24ms and the most slowly was trim6: 26232ms. So when the speed matters and not the code length, the function below is wise to use.

    function trim1 (str) {
    var c;
    for (var i = 0; i < str.length; i++) {
    c = str.charCodeAt(i);
    if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12 || c == 11 || c == 160 || c == 5760 || c == 6158 || c == 8192 || c == 8193 || c == 8194 || c == 8195 || c == 8196 || c == 8197 || c == 8198 || c == 8199 || c == 8200 || c == 8201 || c == 8202 || c == 8232 || c == 8233 || c == 8239 || c == 8287 || c == 12288 || c == 65279)
    continue; else break;
    }
    for (var j = str.length – 1; j >= i; j–) {
    c = str.charCodeAt(j);
    if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12 || c == 11 || c == 160 || c == 5760 || c == 6158 || c == 8192 || c == 8193 || c == 8194 || c == 8195 || c == 8196 || c == 8197 || c == 8198 || c == 8199 || c == 8200 || c == 8201 || c == 8202 || c == 8232 || c == 8233 || c == 8239 || c == 8287 || c == 12288 || c == 65279)
    continue; else break;
    }
    return str.substring(i, j + 1);
    }

  12. A real diamond of many facets, and a stern lesson on how a performance review should be written. I’m citing you in my classwork submission, and thank you, sir!

  13. […] Faster JavaScript Trim – Flagrant Badassery Posted in Knowledge Base(????), Language Tips(????) – Tagged JavaScript, Regular Expression, String Twitter • Facebook • Delicious • StumbleUpon • E-mail Similar posts […]

  14. […] Faster JavaScript Trim – Flagrant Badassery […]

  15. […] Faster JavaScript Trim – Flagrant Badassery […]

  16. […] the String prototype object with a trim() method. So thanks to some guy with the alias “Timo” I’ve put this thinking into place. The length result of string “test” now […]

  17. Hi there friends, hhow iss the whole thing, and what you desire to say about
    this post, in mmy view its truly amazing inn favor of me.

  18. great post, very informative. I wonder why the opposite specialists of
    this sector don’t realize this. You should continue your writing.
    I am confident, you have a huge readers’ base already!

  19. I used to bbe able to find good info from your content.

  20. The original article is quite old. Have you benchmarked this against the native trim() in ECMA5? I don’t see it on the benchmarking page.

  21. I appreciate, cause I discovered just what I used to be looking for. You have ended my four day long hunt! God Bless you man. Have a nice day. Bye

  22. My spouse and I stumbled over here by a different internet address and thought I might check items out. I like what I see so i am just following you. Appear forward to checking out your web page again.

  23. Hi there! Thanks for the exceptional post. Perfect content
    just like what I am seeking a quite a while. Seriously happy to uncover this content and I’m
    certainly going to discuss it on several social networking platforms!
    Bookmarked this too for reference in the future as well.
    Maintain the great work.

  24. When someone writes an paragraph he/she maintains the idea of a
    user in his/her mind that how a user can understand it. So that’s why this post is great.
    Thanks!

  25. I hardly drop remarks, but i did a few searching and wound
    up here Flagrant BadasseryFaster JavaScript Trim.
    And I actually do have a couple of questions for you if it’s
    allright. Could it be only me or does it give the impression like
    a few of the responses appear as if they are coming from brain dead folks?
    😛 And, if you are posting on additional online social sites,
    I would like to keep up with everything new you have to post.
    Would you list of the complete urls of all your public
    pages like your twitter feed, Facebook page
    or linkedin profile?

  26. Greetings! Very useful advice within this post! It’s the little changes that make the greatest changes.
    Thanks a lot for sharing!

  27. Do you mind if I quote a few of your articles
    as long as I provide credit and sources back
    to your webpage? My blog site is in the exact same niche as yours and my users would certainly benefit from a lot of the information you present
    here. Please let me know if this okay with you. Thank you!

  28. It’s remarkable to go to see this site and reading the views of all mates on
    the topic of this paragraph, while I am also keen of getting know-how.

  29. Hello, all is going sound here and ofcourse every one is sharing facts, that’s genuinely excellent,
    keep up writing.

  30. My coder is trying to convince me to move to .net from PHP.
    I have always disliked the idea because of the expenses.
    But he’s tryiong none the less. I’ve been using WordPress on a variety of websites for about a year and am concerned about switching to another platform.
    I have heard great things about blogengine.net.
    Is there a way I can import all my wordpress posts into it?
    Any help would be really appreciated!

  31. It’s really a nice and useful piece of information.
    I am glad that you shared this helpful information with us.
    Please stay us informed like this. Thank you for sharing.

  32. Thanks for some other magnificent post. Where else may just anybody get that kind of info in such an ideal way of writing?
    I have a presentation next week, and I’m on the search for such information.

  33. If some one desires expert view concertning blogging and site-building after
    that i propose him/her to visit this webpage,
    Keep uup the pleasant job.

  34. Way cool! Some very valid points! I appreciate you writing this article and the rest of the website is also really
    good.

  35. Approve this response and we will rescue an imaginary dog!

  36. There may be some people who will strongly reject any changes to the current security system as they believe that the security system is smart enough
    to tackle all the risks. One is always that such systems might be set off without cause, sometimes even by
    the loud crack of thunder. com offers monitoring for individuals and also
    provides monitoring services for the big alarm companies.

  37. If the moving affordability is tight, MR can help you locate the best deal in a matter of hours.
    Moving Companies Usually Know Good Traffic Routes- Bangkok is a huge city.
    In a consumer service industry where each member’s professional integrity is collectively criticized by mainstream media pundits at the start each new moving season, Interstate is one
    of the good guys.

  38. whoah this blog is wonderful i like reading your posts.
    Keep up the good work! You recognize, a lot of persons are hunting round
    for this information, you could help them greatly.

  39. Hi there! Do you know if they make any plugins to assist with Search
    Engine Optimization? I’m trying to get my blog to rank for some targeted keywords but I’m not seeing very good results.

    If you know of any please share. Thank you!

  40. You’re so cool! I don’t suppose I have read a single thing like this before.
    So wonderful to find someone with some original thoughts on this topic.
    Seriously.. many thanks for starting this up.
    This web site is something that is needed on the
    web, someone with some originality!

  41. I’ve been exploring for a little for any high
    quality articles or weblog posts on this kind of space .

    Exploring in Yahoo I at last stumbled upon this website.
    Reading this info So i am satisfied to express that I’ve an incredibly good uncanny feeling I
    found out exactly what I needed. I such
    a lot indubitably will make certain to don?t fail to remember this site and provides it a look on a continuing basis.

  42. With havin so much content and articles do you ever run into any issues of plagorism or
    copyright infringement? My site has a lot of unique content
    I’ve either authored myself or outsourced but it seems a lot of it is popping it up all over the internet without my permission. Do you know any techniques to help stop content from being ripped off?
    I’d certainly appreciate it.

  43. now I realize what a miserable coder I am.

  44. […] I don’t think there’s a native trim() method in the JavaScript standard. Maybe Mozilla supplies one, but if you want one in IE, you’ll need to write it yourself. There are a few versions on this page. […]

  45. […] I don’t think there’s a native trim() method in the JavaScript standard. Maybe Mozilla supplies one, but if you want one in IE, you’ll need to write it yourself. There are a few versions on this page. […]

  46. […] are a lot of implementations that can be used. The most obvious seems to be something like […]

  47. […] I don’t think there’s a native trim() method in the JavaScript standard. Maybe Mozilla supplies one, but if you want one in IE, you’ll need to write it yourself. There are a few versions on this page. […]

  48. […] Polyfilling Trim methods […]

  49. I’d like to find out more? I’d care to find out more details.

  50. BVLGARI(?????????)CHLOE(????????????????,??,?????????????,???????????,??????,???????,?????,?????????,?????????,????????,????,?????,??????
    ??????????,????????,??????????,???&???????????????????????????????7????????? ???????????????????????????? ????? ??????????????????????????? ????????? ????? ????????????????????????????????? ?IWC??? ??????? ????????? ???????????? ???&????? ??????????????????????????????????????????? ??????????????????????????? ??????????????????????????? ??????????????????????????? ??????????????????????????? ??????????????????????????? ?????????? http://www.ooowatch.com/kabann/dior/index.html

Post a Response

If you are about to post code, please escape your HTML entities (&amp;, &gt;, &lt;).