'How to remove specific character surrounding a string?

I have this string:

var str = "? this is a ? test ?";

Now I want to get this:

var newstr = "this is a ? test";

As you see I want to remove just those ? surrounding (in the beginning and end) that string (not in the middle of string). How can do that using JavaScript?

Here is what I have tried:

var str = "? this is a ? test ?";
var result = str.trim("?");
document.write(result);

So, as you see it doesn't work. Actually I'm a PHP developer and trim() works well in PHP. Now I want to know if I can use trim() to do that in JS.


It should be noted I can do that using regex, but to be honest I hate regex for this kind of jobs. Anyway is there any better solution?


Edit: As this mentioned in the comment, I need to remove both ? and whitespaces which are around the string.



Solution 1:[1]

Search for character mask and return the rest without.

This proposal the use of the bitwise not ~ operator for checking.

~ is a bitwise not operator. It is perfect for use with indexOf(), because indexOf returns if found the index 0 ... n and if not -1:

value  ~value   boolean
 -1  =>   0  =>  false
  0  =>  -1  =>  true
  1  =>  -2  =>  true
  2  =>  -3  =>  true
  and so on 

function trim(s, mask) {
    while (~mask.indexOf(s[0])) {
        s = s.slice(1);
    }
    while (~mask.indexOf(s[s.length - 1])) {
        s = s.slice(0, -1);
    }
    return s;
}

console.log(trim('??? this is a ? test ?', '? '));
console.log(trim('abc this is a ? test abc', 'cba '));

Solution 2:[2]

Simply use:

let text = '?? something ? really ??'
text = text.replace(/^([?]*)/g, '')
text = text.replace(/([?]*)$/g, '')

console.log(text)

Solution 3:[3]

A possible solution would be to use recursive functions to remove the unwanted leading and trailing characters. This doesn't use regular expressions.

function ltrim(char, str) {
    if (str.slice(0, char.length) === char) {
        return ltrim(char, str.slice(char.length));
    } else {
        return str;
    }
}

function rtrim(char, str) {
    if (str.slice(str.length - char.length) === char) {
        return rtrim(char, str.slice(0, 0 - char.length));
    } else {
        return str;
    }
}

Of course this is only one of many possible solutions. The function trim would use both ltrim and rtrim.

The reason that char is the first argument and the string that needs to be cleaned the second, is to make it easier to change this into a functional programming style function, like so (ES 2015):

function ltrim(char) {
    (str) => {
        <body of function>
    }
}

// No need to specify str here
function ltrimSpaces = ltrim(' ');

Solution 4:[4]

No regex:

uberTrim = s => s.length >= 2 && (s[0] === s[s.length - 1])?
  s.slice(1, -1).trim() 
  : s;

Step-by-step explanation:

  1. Check if the string is at least 2 characters long and if it is surrounded by a specific character;
  2. If it is, then first slice it to remove the surrounding characters then trim it to remove whitespaces;
  3. If not just return it.

In case you're weirded out by that syntax, it's an Arrow Function and a ternary operator.
The parenthesis are superfluous in the ternary by the way.

Example use:

uberTrim(''); // ''
uberTrim(' Plop! '); //'Plop!'
uberTrim('! ...What is Plop?!'); //'...What is Plop?'

Solution 5:[5]

Simple approach using Array.indexOf, Array.lastIndexOf and Array.slice functions:

Update: (note: the author has requested to trim the surrounding chars)

function trimChars(str, char){
    var str = str.trim();

    var checkCharCount = function(side) {
        var inner_str = (side == "left")? str : str.split("").reverse().join(""),
            count = 0;

        for (var i = 0, len = inner_str.length; i < len; i++) {
            if (inner_str[i] !== char) {
                break;
            }
            count++;
        }
        return (side == "left")? count : (-count - 1);
    };

    if (typeof char === "string" 
            && str.indexOf(char) === 0
            && str.lastIndexOf(char, -1) === 0) {
        str = str.slice(checkCharCount("left"), checkCharCount("right")).trim();
    }

    return str;
}

var str = "???? this is a ? test ??????";

console.log(trimChars(str, "?"));   // "this is a ? test"

Solution 6:[6]

Here is one way to do it which checks for index-out-of-bounds and makes only a single call to substring:

String.prototype.trimChars = function(chars) {
  var l = 0;
  var r = this.length-1;
  while(chars.indexOf(this[l]) >= 0 && l < r) l++;
  while(chars.indexOf(this[r]) >= 0 && r >= l) r--;
  return this.substring(l, r+1);
};

Example:

var str = "? this is a ? test ?";

str.trimChars(" ?");  // "this is a ? test"

Solution 7:[7]

to keep this question up to date using an ES6 approach:

I liked the bitwise method but when readability is a concern too then here's another approach.

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

Solution 8:[8]

Javascript's trim method only remove whitespaces, and takes no parameters. For a custom trim, you will have to make your own function. Regex would make a quick solution for it, and you can find an implementation of a custom trim on w3schools in case you don't want the trouble of going through the regex creation process. (you'd just have to adjust it to filter ? instead of whitespace

Solution 9:[9]

Using regex

'? this is a ? test ?'.replace(/^[? ]*(.*?)[? ]*$/g, '$1')

You may hate regex but after finding a solution you will feel cool :)

Solution 10:[10]

This in one line of code which returns your desire output:

"? this is a ? test ?".slice(1).slice(0,-1).trim();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Michal Karzel
Solution 3
Solution 4
Solution 5
Solution 6 ericman314
Solution 7 Robin F.
Solution 8 Bettorun
Solution 9 Anupam Ghosh
Solution 10 4b0