Trim specific character from a string
Asked Answered
J

23

232

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!

Jamshid answered 2/10, 2014 at 7:40 Comment(1)
I'm so perplexed that the trim function isn't accepting an argument in ECMAScript.Bluefarb
D
285

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\]\}"
Dorothi answered 11/9, 2015 at 5:37 Comment(4)
Replace the two ifs in the "general solution" with regex escape for it to safely support multiple characters: c = c.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')Inconvenience
I think you'd better do the full escape to be safe, another character that will cause problems is -Inconvenience
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 caseInconvenience
@Inconvenience 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.Dorothi
H
83

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');
Holt answered 22/3, 2019 at 3:10 Comment(3)
I prefer this solution because it is, in fact, actually efficient, rather than just short.Adallard
I agree it should be preferred. Supersedes an answer I had given.Crowe
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.Scirrhus
O
50

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, "|") + "'" );
Overboard answered 2/10, 2014 at 8:15 Comment(4)
This would be a fun test for the garbage collector, but I wouldn't recommend subjecting your clients to it.Milkfish
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.Bikini
@Bikini 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.Pulmonate
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...Benumb
U
25

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
Unstopped answered 2/10, 2014 at 8:21 Comment(0)
B
23

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.

Bunker answered 11/4, 2019 at 21:57 Comment(6)
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, '#//'));Crowe
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).Cocoon
You're right, that's what I tried to say with "no repetition of the chars off the edges".Bunker
Great solution! I need to clear path from leading and trailing slashes ```` path.split('/').filter(Boolean).join('/') ```` This works brilliant to me :-) Many thanks!Abshier
this is not a trim, this is a char replaceFibroid
@AlexanderGavriliuk that was exactly my use-case, too :-)Bunker
G
17

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);
}
Goldstein answered 10/4, 2017 at 21:54 Comment(16)
I know regexes are overkill here, but why would you choose this particular implementation?Kirkuk
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.Goldstein
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.Kirkuk
:) 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://mcmap.net/q/46980/-how-can-i-process-each-letter-of-text-using-javascript 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.Goldstein
@RobinF. you think findIndex() and reverse() does not contain loops? Think again.Interpellate
@Interpellate 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.Goldstein
@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.Interpellate
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.Bumkin
Why do we need whole reverse thing? Isn't arr.lastIndexOf(character) enough?Glutelin
@AndriiM4n0w4R yes but then we'd have to replace the string.substring function with the string.substr functionGoldstein
@RobinF. Nope. Actually, we can remove even this then: string.length - last - 1. Please look here: 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.Glutelin
You're correct @AndriiM4n0w4R i didn't test the improved fn well enough. I've edited the second version.Goldstein
@RobinF. looks working now, although the performance isn't the best =). I removed the downvote. Thanks for not abandoning this )Glutelin
@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.Goldstein
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)Goldstein
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/secHolt
T
14

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.

Tedesco answered 30/11, 2017 at 8:37 Comment(1)
var trimChars = (x, y) => x.replace(new RegExp(`^[${y}]+|[${y}]+$`, "g"),""); - one linerInattention
S
5
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.

Search answered 30/9, 2021 at 17:51 Comment(0)
C
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.

Condorcet answered 28/3, 2019 at 18:39 Comment(4)
@Crowe not the sameCompaction
@devi I can only agree. thank you for the comment. good answer looking into a community supported tool.Crowe
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.Scirrhus
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.Pulmonate
S
0

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
Springhead answered 23/8, 2018 at 8:41 Comment(0)
G
0

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);
};
Gramme answered 18/2, 2019 at 20:45 Comment(0)
C
0

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.

Cocky answered 11/4, 2020 at 10:26 Comment(0)
S
0

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');
Succentor answered 16/8, 2022 at 8:18 Comment(0)
P
0

Improved typescript version of @JasonLarke answer:

export const trimChars = (input: string, chars: string) => {
  let start = 0
  let end = input.length

  while (start < end && chars.includes(input[start])) {
    start += 1
  }

  while (end > start && chars.includes(input[end - 1])) {
    end -= 1
  }

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

Usage:

trimChars(' .,hello;', ' ,.;') // -> 'hello'
Philanthropy answered 26/1, 2024 at 10:42 Comment(0)
I
0

All the top answers only escapes special characters that are only one characters. Here is my modification of the function above, that can:

  1. use multiple characters as input
  2. escape every character from the input
const characterToEscape = new Set(']', '^', '\\', '|', '-');
function trim(s, c) {
    c = c
        .split('')
        .map(component =>
            characterToEscape.has(component) || component === '\\' ? `\\${component}` : component,
        )
        .join('"');

    return s.replace(new RegExp(`^[${c}]+|[${c}]+$`, 'g'), '');
}
Irisirisa answered 26/3, 2024 at 11:33 Comment(0)
L
-1

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
}
Lisa answered 17/7, 2018 at 23:12 Comment(0)
L
-1
function trim(text, val) {
    return text.replace(new RegExp('^'+val+'+|'+val+'+$','g'), '');
}
Lobelia answered 20/5, 2019 at 8:44 Comment(0)
H
-1
"|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.

Hoeve answered 21/3, 2020 at 17:47 Comment(0)
P
-1

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$%'));
Poulson answered 19/10, 2020 at 11:44 Comment(0)
R
-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.

Rockandroll answered 2/10, 2014 at 7:43 Comment(3)
is there a way to remove | only at the beginning / end?Jamshid
I actually think this post will get you the most up to speed on your question: #20196588Rockandroll
@Jamshid Sure... Throw in a $ like this for only at end: "||spam|||".replace(/\|+$/g, "") or a ^ like this for only at start: "||spam|||".replace(/^\|+/g, "")Scirrhus
D
-3

try:

console.log(x.replace(/\|/g,''));
Daegal answered 2/10, 2014 at 7:45 Comment(0)
F
-3
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);
};
Filippo answered 12/1, 2018 at 9:32 Comment(2)
It only remove one occurrence, but does not trim until the character is fully trimmedPackard
Don't override the default string prototype or you're asking for trouble later on. Create your own separate functions elsewhere.Immerse
Z
-5

Try this method:

var a = "anan güzel mi?";
if (a.endsWith("?"))   a = a.slice(0, -1);  
document.body.innerHTML = a;
Zeitler answered 29/9, 2019 at 19:34 Comment(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.Kristankriste

© 2022 - 2025 — McMap. All rights reserved.