218

What's the JavaScript equivalent to this C# Method:

var x = "|f|oo||"; 
var y = x.Trim('|'); //  "f|oo"

C# trims the selected character only at the beginning and end of the string!

fubo
  • 44,811
  • 17
  • 103
  • 137

22 Answers22

278

One line is enough:

var x = '|f|oo||';
var y = x.replace(/^\|+|\|+$/g, '');
document.write(x + '<br />' + y);
^     beginning of the string
\|+   pipe, one or more times
|     or
\|+   pipe, one or more times
$     end of the string

A general solution:

function trim (s, c) {
  if (c === "]") c = "\\]";
  if (c === "^") c = "\\^";
  if (c === "\\") c = "\\\\";
  return s.replace(new RegExp(
    "^[" + c + "]+|[" + c + "]+$", "g"
  ), "");
}

chars = ".|]\\^";
for (c of chars) {
  s = c + "foo" + c + c + "oo" + c + c + c;
  console.log(s, "->", trim(s, c));
}

Parameter c is expected to be a character (a string of length 1).

As mentionned in the comments, it might be useful to support multiple characters, as it's quite common to trim multiple whitespace-like characters for example. To do this, MightyPork suggests to replace the ifs with the following line of code:

c = c.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');

This part [-/\\^$*+?.()|[\]{}] is a set of special characters in regular expression syntax, and $& is a placeholder which stands for the matching character, meaning that the replace function escapes special characters. Try in your browser console:

> "{[hello]}".replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
"\{\[hello\]\}"
  • 1
    Replace the two ifs in the "general solution" with regex escape for it to safely support multiple characters: `c = c.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')` – MightyPork Mar 01 '21 at 09:02
  • @MightyPork Only `^` was missing. Thanks. –  Mar 02 '21 at 13:36
  • I think you'd better do the full escape to be safe, another character that will cause problems is `-` – MightyPork Mar 03 '21 at 14:04
  • 1
    Oh, and it still doesn't support multiple characters. Imagine you want to trim tabs and spaces so you use `"\t "`, that sort of use case – MightyPork Mar 03 '21 at 14:10
  • @MightyPork I tried with `-` (`trim("-foo--oo---", '-')`), and it doesn't seem to cause any problem. Anyway, The idea of trimming multiple types of space characters sounds interesting enough, I'm going to summarize your idea below the original answer. –  Mar 04 '21 at 14:43
  • Regex explanations like in your answer are beautiful! Enough to turn a bad day good! TY ✌️ – Davey Feb 08 '22 at 09:27
72

Update: Was curious around the performance of different solutions and so I've updated a basic benchmark here: https://www.measurethat.net/Benchmarks/Show/12738/0/trimming-leadingtrailing-characters

Some interesting and unexpected results running under Chrome. https://www.measurethat.net/Benchmarks/ShowResult/182877

+-----------------------------------+-----------------------+
| Test name                         | Executions per second |
+-----------------------------------+-----------------------+
| Index Version (Jason Larke)       | 949979.7 Ops/sec      |
| Substring Version (Pho3niX83)     | 197548.9 Ops/sec      |
| Regex Version (leaf)              | 107357.2 Ops/sec      |
| Boolean Filter Version (mbaer3000)| 94162.3 Ops/sec       |
| Spread Version (Robin F.)         | 4242.8 Ops/sec        |
+-----------------------------------+-----------------------+

Please note; tests were carried out on only a single test string (with both leading and trailing characters that needed trimming). In addition, this benchmark only gives an indication of raw speed; other factors like memory usage are also important to consider.


If you're dealing with longer strings I believe this should outperform most of the other options by reducing the number of allocated strings to either zero or one:

function trim(str, ch) {
    var start = 0, 
        end = str.length;

    while(start < end && str[start] === ch)
        ++start;

    while(end > start && str[end - 1] === ch)
        --end;

    return (start > 0 || end < str.length) ? str.substring(start, end) : str;
}

// Usage:
trim('|hello|world|', '|'); // => 'hello|world'

Or if you want to trim from a set of multiple characters:

function trimAny(str, chars) {
    var start = 0, 
        end = str.length;

    while(start < end && chars.indexOf(str[start]) >= 0)
        ++start;

    while(end > start && chars.indexOf(str[end - 1]) >= 0)
        --end;

    return (start > 0 || end < str.length) ? str.substring(start, end) : str;
}

// Usage:
trimAny('|hello|world   ', [ '|', ' ' ]); // => 'hello|world'
// because '.indexOf' is used, you could also pass a string for the 2nd parameter:
trimAny('|hello| world  ', '| '); // => 'hello|world'

EDIT: For fun, trim words (rather than individual characters)

// Helper function to detect if a string contains another string
//     at a specific position. 
// Equivalent to using `str.indexOf(substr, pos) === pos` but *should* be more efficient on longer strings as it can exit early (needs benchmarks to back this up).
function hasSubstringAt(str, substr, pos) {
    var idx = 0, len = substr.length;

    for (var max = str.length; idx < len; ++idx) {
        if ((pos + idx) >= max || str[pos + idx] != substr[idx])
            break;
    }

    return idx === len;
}

function trimWord(str, word) {
    var start = 0,
        end = str.length,
        len = word.length;

    while (start < end && hasSubstringAt(str, word, start))
        start += word.length;

    while (end > start && hasSubstringAt(str, word, end - len))
        end -= word.length

    return (start > 0 || end < str.length) ? str.substring(start, end) : str;
}

// Usage:
trimWord('blahrealmessageblah', 'blah');
Jason Larke
  • 5,289
  • 25
  • 28
  • 5
    I prefer this solution because it is, in fact, actually efficient, rather than just short. – tekHedd Nov 22 '19 at 21:12
  • 1
    I agree it should be preferred. Supersedes an answer I had given. – TamusJRoyce Jan 22 '20 at 11:43
  • If you wanted to go full C# trim replacement, you might present this answer as an extension method (prototype extension?)... `String.prototype.trimChar = String.prototype.trimChar || function (chr) { // ...` You might also sniff for `trim(undefined, "|")` if you don't make it an extension. – ruffin Apr 05 '21 at 22:26
49

If I understood well, you want to remove a specific character only if it is at the beginning or at the end of the string (ex: ||fo||oo|||| should become foo||oo). You can create an ad hoc function as follows:

function trimChar(string, charToRemove) {
    while(string.charAt(0)==charToRemove) {
        string = string.substring(1);
    }

    while(string.charAt(string.length-1)==charToRemove) {
        string = string.substring(0,string.length-1);
    }

    return string;
}

I tested this function with the code below:

var str = "|f|oo||";
$( "#original" ).html( "Original String: '" + str + "'" );
$( "#trimmed" ).html( "Trimmed: '" + trimChar(str, "|") + "'" );
Yves M.
  • 29,855
  • 23
  • 108
  • 144
Pho3niX83
  • 644
  • 4
  • 3
  • 10
    This would be a fun test for the garbage collector, but I wouldn't recommend subjecting your clients to it. – Sorensen Oct 20 '19 at 19:05
  • 1
    Why I like this answer most is: I can read it and I can understand it. The garbage collector will work proper. And with this solution I can apply string instead of chars and I can still read and understand what it does without diving into Regex syntax. – Nasenbaer Sep 04 '21 at 12:26
  • @Nasenbaer at least you would want to handle edge cases, where string has no more chars because all were removed. Not sure if string.charAt(-1) works the same on all current and future implementations in browsers. – SwissCoder Feb 16 '22 at 09:19
  • One edge case that might be good to handle is if the passed string is undefined. Two ways to handle it would be adding at the top: string=String(string) ...or... if (!string) string='' each with slightly different behaviour... – Mikael Lindqvist Aug 17 '23 at 04:31
26

You can use a regular expression such as:

var x = "|f|oo||";
var y = x.replace(/^\|+|\|+$/g, "");
alert(y); // f|oo

UPDATE:

Should you wish to generalize this into a function, you can do the following:

var escapeRegExp = function(strToEscape) {
    // Escape special characters for use in a regular expression
    return strToEscape.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
};

var trimChar = function(origString, charToTrim) {
    charToTrim = escapeRegExp(charToTrim);
    var regEx = new RegExp("^[" + charToTrim + "]+|[" + charToTrim + "]+$", "g");
    return origString.replace(regEx, "");
};

var x = "|f|oo||";
var y = trimChar(x, "|");
alert(y); // f|oo
fregante
  • 29,050
  • 14
  • 119
  • 159
neelsg
  • 4,802
  • 5
  • 34
  • 58
24

A regex-less version which is easy on the eye:

const trim = (str, chars) => str.split(chars).filter(Boolean).join(chars);

For use cases where we're certain that there's no repetition of the chars off the edges.

mbaer3000
  • 479
  • 4
  • 5
  • quite interesting... so split returns undefined element for equal to each delimiter that is split `const trim = (str, chars) => str.split(chars).filter(x => { Boolean(x); console.log(typeof(x), x, Boolean(x)); }).join(chars); const str = "#//#//abc#//test#//end#//"; console.log(trim(str, '#//'));` – TamusJRoyce Jan 23 '20 at 01:52
  • This doesn't work if there are duplicate chars in the middle of the string. `trim("|first||last|", "|")` returns `first|last` (only one pipe in the middle). – Cornelius Oct 10 '20 at 06:32
  • You're right, that's what I tried to say with "no repetition of the chars off the edges". – mbaer3000 Oct 11 '20 at 09:07
  • Great solution! I need to clear path from leading and trailing slashes ```` path.split('/').filter(Boolean).join('/') ```` This works brilliant to me :-) Many thanks! – Alexander Gavriliuk Dec 01 '20 at 22:51
  • 1
    this is not a trim, this is a char replace – albanx Oct 05 '21 at 15:50
  • @AlexanderGavriliuk that was exactly my use-case, too :-) – mbaer3000 Oct 24 '21 at 19:20
18

to keep this question up to date:

here is an approach i'd choose over the regex function using the ES6 spread operator.

function trimByChar(string, character) {
  const first = [...string].findIndex(char => char !== character);
  const last = [...string].reverse().findIndex(char => char !== character);
  return string.substring(first, string.length - last);
}

Improved version after @fabian 's comment (can handle strings containing the same character only)

function trimByChar1(string, character) {
  const arr = Array.from(string);
  const first = arr.findIndex(char => char !== character);
  const last = arr.reverse().findIndex(char => char !== character);
  return (first === -1 && last === -1) ? '' : string.substring(first, string.length - last);
}
Robin F.
  • 1,137
  • 11
  • 19
  • 3
    I know regexes are overkill here, but why would you choose this particular implementation? – Nicholas Shanks Oct 08 '18 at 14:54
  • 2
    this implementation because I personally find it well readable. no regex simply because the decision "tree" within regex engines is much bigger. and especially because the regexes used for trimming contain query characters which lead to backtracking within the regex engine. such engines often compile the pattern into byte-code, resembling machine instructions. the engine then executes the code, jumping from instruction to instruction. when an instruction fails, it then back-tracks to find another way to match the input. ergo a lot more going on than nec. – Robin F. Dec 14 '18 at 11:12
  • Thanks for replying, although I wanted you to explain why you'd choose this over other non-regex ways of doing it — I was hoping for more that just "I find it readable", I suppose. – Nicholas Shanks Dec 15 '18 at 16:44
  • :) sorry to disappoint but all the other solutions contain for loops (for with strl.length and index, for of, etc.) with a closure for saving the findings...you can look at some here: https://stackoverflow.com/a/41573058/5242207 also this solutions is quite fast since as soon as the index is found, the find functions stops iterating through the string. the reverse should come cheap on an optimization level too since we can just iterate from the back to the front and dont need to sort the array. – Robin F. Dec 15 '18 at 16:51
  • 1
    @RobinF. you think findIndex() and reverse() does not contain loops? Think again. – Andrew Apr 14 '20 at 16:25
  • @Andrew internally it, of course, iterates through the items too - until the index is found - which is quite fast because it can terminate as soon it has found the item. syntax wise you don't have to write these loops yourself tough. – Robin F. Apr 15 '20 at 13:00
  • @RobinF. when you have 'trim_by_char()', or any other function you have made yourself, you don't have to write any loops or anything else as well. Just use the function. – Andrew Apr 15 '20 at 14:27
  • 1
    Two annotations: A string containg only of the character to be trimmed will not be trimmed at all. The other point is: Exploding the string into an array with the spread operator will confuse babel and transform it into `[].concat(string)` which is not the desired outcome. Using `Array.from(string)` will work. – Fabian Jul 06 '20 at 07:48
  • 1
    Why do we need whole `reverse` thing? Isn't [`arr.lastIndexOf(character)`](https://www.w3schools.com/JSREF/jsref_lastindexof_array.asp) enough? – Andrii M4n0w4R Sep 01 '20 at 14:26
  • @AndriiM4n0w4R yes but then we'd have to replace the string.substring function with the string.substr function – Robin F. Sep 03 '20 at 15:56
  • @RobinF. Nope. Actually, we can remove even this then: `string.length - last - 1`. Please look here: https://jsfiddle.net/0df3ngzj/ . Sorry but after I've dived into this, I have to downvote because _this answer does NOT answer the question_ - see the fiddle, the result just contains text between first and last occurrence of a character, which is not how `trim` works. See @Jason Larke 's answer - it is most efficient and does the right thing. – Andrii M4n0w4R Sep 07 '20 at 09:42
  • You're correct @AndriiM4n0w4R i didn't test the improved fn well enough. I've edited the second version. – Robin F. Sep 07 '20 at 15:17
  • @RobinF. looks working now, although the performance isn't the best =). I removed the downvote. Thanks for not abandoning this ) – Andrii M4n0w4R Sep 08 '20 at 08:49
  • @AndriiM4n0w4R thanks! btw. I haven't tested the performance - but I guess that the Array.from under the hood is just a char array null terminated thus not much to do and the Array.reverse might be optmised -under the hood just looping the array in reverse order - and the findindex aborts the loop as soon as the condition matches thus it might be quite performant actually. To be tested though. – Robin F. Sep 08 '20 at 09:57
  • tested that quickly - still ~2x slower than the while solution. The reason is the Array.from. If we slightly modify the fn so the array is only created once before the fn runs we get similar speeds: Checked test: findIndexVersion x 2,170,740 ops/sec ±11.04% (45 runs sampled) Checked test: whileVersion x 2,579,448 ops/sec ±14.28% (41 runs sampled) – Robin F. Sep 08 '20 at 23:41
  • Added some benchmarks to my answer on a long (20,400 character) string. Unfortunately I grabbed the earlier (using spread) version of your function when I added the benchmarks but reprofiling with the updated version only added another 2k ops/sec – Jason Larke Apr 23 '21 at 03:03
13

This can trim several characters at a time:

function trimChars (str, c) {
  var re = new RegExp("^[" + c + "]+|[" + c + "]+$", "g");
  return str.replace(re,"");
}

var x = "|f|oo||"; 
x =  trimChars(x, '|'); // f|oo

var y = "..++|f|oo||++..";
y = trimChars(y, '|.+'); // f|oo

var z = "\\f|oo\\"; // \f|oo\

// For backslash, remember to double-escape:
z = trimChars(z, "\\\\"); // f|oo

For use in your own script and if you don't mind changing the prototype, this can be a convenient "hack":

String.prototype.trimChars = function (c) {
  var re = new RegExp("^[" + c + "]+|[" + c + "]+$", "g");
  return this.replace(re,"");
}

var x = "|f|oo||"; 
x =  x.trimChars('|'); // f|oo

Since I use the trimChars function extensively in one of my scripts, I prefer this solution. But there are potential issues with modifying an object's prototype.

marlar
  • 3,858
  • 6
  • 37
  • 60
4

If you define these functions in your program, your strings will have an upgraded version of trim that can trim all given characters:

String.prototype.trimLeft = function(charlist) {
 if (charlist === undefined)
 charlist = "\s";

 return this.replace(new RegExp("^[" + charlist + "]+"), "");
};

String.prototype.trim = function(charlist) {
 return this.trimLeft(charlist).trimRight(charlist);
};

String.prototype.trimRight = function(charlist) {
 if (charlist === undefined)
 charlist = "\s";

 return this.replace(new RegExp("[" + charlist + "]+$"), "");
};

var withChars = "/-center-/"
var withoutChars = withChars.trim("/-")
document.write(withoutChars)

Source

https://www.sitepoint.com/trimming-strings-in-javascript/

Chris Redford
  • 16,982
  • 21
  • 89
  • 109
  • Nice solution, just people be carefull about overrading native functions like trim. To avoid unexpected issues you could use different naming convention (`String.prototype.MyOwn_trim`) or just create simple function outside `String.prototype`. – gsubiran Dec 10 '20 at 20:44
3
const trim = (str, char) => {
    let i = 0;
    let j = str.length-1;
    while (str[i] === char) i++;
    while (str[j] === char) j--;
    return str.slice(i,j+1);
}
console.log(trim('|f|oo|', '|')); // f|oo

Non-regex solution. Two pointers: i (beginning) & j (end). Only move pointers if they match char and stop when they don't. Return remaining string.

Toni
  • 1,555
  • 4
  • 15
  • 23
2

I would suggest looking at lodash and how they implemented the trim function.

See Lodash Trim for the documentation and the source to see the exact code that does the trimming.

I know this does not provide an exact answer your question, but I think it's good to set a reference to a library on such a question since others might find it useful.

drew7721
  • 1,622
  • 18
  • 20
  • 1
    @TamusJRoyce not the same – gdbdable May 29 '20 at 22:25
  • @devi I can only agree. thank you for the comment. good answer looking into a community supported tool. – TamusJRoyce May 30 '20 at 03:59
  • lodash is MIT licensed. To make this a better answer, I'd suggest bringing in the source and presenting it with what was useful from it for you here. Then if that link changes, the good information remains in your answer. – ruffin Apr 05 '21 at 21:40
  • The current trim implementation in lodash makes use of 6 other lodash functions, which again make use of other lodash functions too... If you go that path, you can just as well import lodash in your project. – SwissCoder Feb 16 '22 at 14:11
1

This one trims all leading and trailing delimeters

const trim = (str, delimiter) => {
  const pattern = `[^\\${delimiter}]`;
  const start = str.search(pattern);
  const stop = str.length - str.split('').reverse().join('').search(pattern);
  return str.substring(start, stop);
}

const test = '||2|aaaa12bb3ccc|||||';
console.log(trim(test, '|')); // 2|aaaa12bb3ccc
Dmitriy Botov
  • 2,623
  • 1
  • 15
  • 12
1

I like the solution from @Pho3niX83...

Let's extend it with "word" instead of "char"...

function trimWord(_string, _word) {

    var splitted = _string.split(_word);

    while (splitted.length && splitted[0] === "") {
        splitted.shift();
    }
    while (splitted.length && splitted[splitted.length - 1] === "") {
        splitted.pop();
    }
    return splitted.join(_word);
};
foxontherock
  • 1,776
  • 1
  • 13
  • 16
1

The best way to resolve this task is (similar with PHP trim function):

function trim( str, charlist ) {
  if ( typeof charlist == 'undefined' ) {
    charlist = '\\s';
  }
  
  var pattern = '^[' + charlist + ']*(.*?)[' + charlist + ']*$';
  
  return str.replace( new RegExp( pattern ) , '$1' )
}

document.getElementById( 'run' ).onclick = function() {
  document.getElementById( 'result' ).value = 
  trim( document.getElementById( 'input' ).value,
  document.getElementById( 'charlist' ).value);
}
<div>
  <label for="input">Text to trim:</label><br>
  <input id="input" type="text" placeholder="Text to trim" value="dfstextfsd"><br>
  <label for="charlist">Charlist:</label><br>
  <input id="charlist" type="text" placeholder="Charlist" value="dfs"><br>
  <label for="result">Result:</label><br>
  <input id="result" type="text" placeholder="Result" disabled><br>
  <button type="button" id="run">Trim it!</button>
</div>

P.S.: why i posted my answer, when most people already done it before? Because i found "the best" mistake in all of there answers: all used the '+' meta instead of '*', 'cause trim must remove chars IF THEY ARE IN START AND/OR END, but it return original string in else case.

StoneCountry
  • 151
  • 6
1

Another version to use regular expression.

No or(|) used and no global(g) used.

function escapeRegexp(s) {
  return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

function trimSpecific(value, find) {
  const find2 = escapeRegexp(find);
  return value.replace(new RegExp(`^[${find2}]*(.*?)[${find2}]*$`), '$1')
}

console.log(trimSpecific('"a"b"', '"') === 'a"b');
console.log(trimSpecific('""ab"""', '"') === 'ab');
console.log(trimSpecific('"', '"') === '');
console.log(trimSpecific('"a', '"') === 'a');
console.log(trimSpecific('a"', '"') === 'a');
console.log(trimSpecific('[a]', '[]') === 'a');
console.log(trimSpecific('{[a]}', '[{}]') === 'a');
doctorgu
  • 616
  • 6
  • 9
0

expanding on @leaf 's answer, here's one that can take multiple characters:

var trim = function (s, t) {
  var tr, sr
  tr = t.split('').map(e => `\\\\${e}`).join('')
  sr = s.replace(new RegExp(`^[${tr}]+|[${tr}]+$`, 'g'), '')
  return sr
}
AmitK
  • 183
  • 1
  • 7
0
function trim(text, val) {
    return text.replace(new RegExp('^'+val+'+|'+val+'+$','g'), '');
}
0
"|Howdy".replace(new RegExp("^\\|"),"");

(note the double escaping. \\ needed, to have an actually single slash in the string, that then leads to escaping of | in the regExp).

Only few characters need regExp-Escaping., among them the pipe operator.

Frank N
  • 9,625
  • 4
  • 80
  • 110
0

const special = ':;"<>?/!`~@#$%^&*()+=-_ '.split("");
const trim = (input) => {
    const inTrim = (str) => {
        const spStr = str.split("");
        let deleteTill = 0;
        let startChar = spStr[deleteTill];
        while (special.some((s) => s === startChar)) {
            deleteTill++;
            if (deleteTill <= spStr.length) {
                startChar = spStr[deleteTill];
            } else {
                deleteTill--;
                break;
            }
        }
        spStr.splice(0, deleteTill);
        return spStr.join("");
    };
input = inTrim(input);
input = inTrim(input.split("").reverse().join("")).split("").reverse().join("");
return input;
};
alert(trim('@#This is what I use$%'));
-2
String.prototype.TrimStart = function (n) {
    if (this.charAt(0) == n)
        return this.substr(1);
};

String.prototype.TrimEnd = function (n) {
    if (this.slice(-1) == n)
        return this.slice(0, -1);
};
Matthias Seifert
  • 2,033
  • 3
  • 29
  • 41
Tobo
  • 15
  • It only remove one occurrence, but does not trim until the character is fully trimmed – KoalaBear Jan 26 '18 at 09:35
  • 1
    Don't override the default string prototype or you're asking for trouble later on. Create your own separate functions elsewhere. – rooby May 16 '19 at 02:00
-3

To my knowledge, jQuery doesnt have a built in function the method your are asking about. With javascript however, you can just use replace to change the content of your string:

x.replace(/|/i, ""));

This will replace all occurences of | with nothing.

Ole Haugset
  • 3,709
  • 3
  • 23
  • 44
  • 1
    is there a way to remove | only at the beginning / end? – fubo Oct 02 '14 at 07:59
  • I actually think this post will get you the most up to speed on your question: http://stackoverflow.com/questions/20196088/jquery-remove-first-and-last-character-of-string – Ole Haugset Oct 02 '14 at 08:08
  • @fubo Sure... Throw in a `$` like this for only at end: `"||spam|||".replace(/\|+$/g, "")` or a `^` like this for only at start: `"||spam|||".replace(/^\|+/g, "")` – ruffin Feb 20 '18 at 19:45
-3

try:

console.log(x.replace(/\|/g,''));
Man Programmer
  • 5,300
  • 2
  • 21
  • 21
-4

Try this method:

var a = "anan güzel mi?";
if (a.endsWith("?"))   a = a.slice(0, -1);  
document.body.innerHTML = a;
Gerrit Bertier
  • 4,101
  • 2
  • 20
  • 36
Ali CAKIL
  • 383
  • 5
  • 21
  • 1
    Why? What does this do? How does it work? Code-only answers are considered low quality on SO. Explain your answer so OP and any future visitors can learn from it. – Don't Panic Sep 30 '19 at 07:59