Shorten string without cutting words in JavaScript
Solution 1
If I understand correctly, you want to shorten a string to a certain length (e.g. shorten "The quick brown fox jumps over the lazy dog"
to, say, 6 characters without cutting off any word).
If this is the case, you can try something like the following:
var yourString = "The quick brown fox jumps over the lazy dog"; //replace with your string.
var maxLength = 6 // maximum number of characters to extract
//trim the string to the maximum length
var trimmedString = yourString.substr(0, maxLength);
//re-trim if we are in the middle of a word
trimmedString = trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(" ")))
Solution 2
There are lots of ways to do it, but a regular expression is a useful one line method:
"this is a longish string of text".replace(/^(.{11}[^\s]*).*/, "$1");
//"this is a longish"
This expressions returns the first 11 (any) characters plus any subsequent non-space characters.
Example script:
<pre>
<script>
var t = "this is a longish string of text";
document.write("1: " + t.replace(/^(.{1}[^\s]*).*/, "$1") + "\n");
document.write("2: " + t.replace(/^(.{2}[^\s]*).*/, "$1") + "\n");
document.write("5: " + t.replace(/^(.{5}[^\s]*).*/, "$1") + "\n");
document.write("11: " + t.replace(/^(.{11}[^\s]*).*/, "$1") + "\n");
document.write("20: " + t.replace(/^(.{20}[^\s]*).*/, "$1") + "\n");
document.write("100: " + t.replace(/^(.{100}[^\s]*).*/, "$1") + "\n");
</script>
Output:
1: this
2: this
5: this is
11: this is a longish
20: this is a longish string
100: this is a longish string of text
Solution 3
I am kind of surprised that for a simple problem like this there are so many answers that are difficult to read and some, including the chosen one, do not work .
I usually want the result string to be at most maxLen
characters.
I also use this same function to shorten the slugs in URLs.
str.lastIndexOf(searchValue[, fromIndex])
takes a second parameter that is the index at which to start searching backwards in the string making things efficient and simple.
// Shorten a string to less than maxLen characters without truncating words.
function shorten(str, maxLen, separator = ' ') {
if (str.length <= maxLen) return str;
return str.substr(0, str.lastIndexOf(separator, maxLen));
}
This is a sample output:
for (var i = 0; i < 50; i += 3)
console.log(i, shorten("The quick brown fox jumps over the lazy dog", i));
0 ""
3 "The"
6 "The"
9 "The quick"
12 "The quick"
15 "The quick brown"
18 "The quick brown"
21 "The quick brown fox"
24 "The quick brown fox"
27 "The quick brown fox jumps"
30 "The quick brown fox jumps over"
33 "The quick brown fox jumps over"
36 "The quick brown fox jumps over the"
39 "The quick brown fox jumps over the lazy"
42 "The quick brown fox jumps over the lazy"
45 "The quick brown fox jumps over the lazy dog"
48 "The quick brown fox jumps over the lazy dog"
And for the slug:
for (var i = 0; i < 50; i += 10)
console.log(i, shorten("the-quick-brown-fox-jumps-over-the-lazy-dog", i, '-'));
0 ""
10 "the-quick"
20 "the-quick-brown-fox"
30 "the-quick-brown-fox-jumps-over"
40 "the-quick-brown-fox-jumps-over-the-lazy"
Solution 4
Everyone seems to forget that indexOf takes two arguments- the string to match, and the character index to start looking from. You can break the string at the first space after 10 characters.
function cutString(s, n){
var cut= s.indexOf(' ', n);
if(cut== -1) return s;
return s.substring(0, cut)
}
var s= "this is a long string i cant display";
cutString(s, 10)
/* returned value: (String)
this is a long
*/
Solution 5
Lodash has a function specifically written for this: _.truncate
const truncate = _.truncate
const str = 'The quick brown fox jumps over the lazy dog'
truncate(str, {
length: 30, // maximum 30 characters
separator: /,?\.* +/ // separate by spaces, including preceding commas and periods
})
// 'The quick brown fox jumps...'
Josh Bedo
I'm a 22 year old developer who loves learning and building new things
Updated on July 08, 2022Comments
-
Josh Bedo almost 2 years
I'm not very good with string manipulation in JavaScript, and I was wondering how you would go about shortening a string without cutting any word off. I know how to use substring, but not indexOf or anything really well.
Say I had the following string:
text = "this is a long string I cant display"
I want to trim it down to 10 characters, but if it doesn't end with a space, finish the word. I don't want the string variable to look like this:
"this is a long string I cant dis"
I want it to finish the word until a space occurs.
-
Chance about 13 yearsyou mean trim a string? try
" too many spaces ".trim()
-
Gromski about 13 yearsSome example input and expected output would help a lot in answering this question.
-
Josh Bedo about 13 yearsalright sorry say i had the string text = "this is a long string i cant display" i want to trim it down to 10 characters but if it doesnt end with a space finish the word i don't want the string variable to look like this "this is a long string i cant dis"
-
-
Josh Bedo about 13 yearsAwesome, i literally googled this question a million ways and could only find a working version for php nothing close to this and involving loops.
-
Hamish about 13 yearsnice one - also easier to make the length variable that my regex solution.
-
Hamish about 13 yearsIt's referring to the first (and only, in this case) sub expression match - the stuff in the brackets. $0 would refer to the whole match, which in this case is the whole string.
-
Josh Bedo about 13 yearsseems to be printing out everything still.
-
Josh Bedo about 13 yearsalright thanks a lot, i'm guessing a max value variable isn't possible?
-
Josh Bedo about 13 yearswill have to try the edited version i just found out .replace doesn't work in jquery functions ex:.each so i have to figure out a way around that.
-
Pointy about 13 years@josh it's absolutely not true that ".replace" does not work in "jQuery functions". There's not even any such thing as a "jQuery function".
-
rjmackay over 10 years@josh You should be able to make max length a variable by using a regexp object:
t.replace(new RegExp("^(.{"+length+"}[^\s]*).*"), "$1")
-
rjmackay over 10 yearsAlso worth noting that this breaks with a multi-line input.. since the regex matches the first line longer than max-length, rather than matching from the start.
-
Beytan Kurt over 10 yearsisn't that should be "maxLength + 1". And if maxLength is greater or equal than the complete sentence length, than the last word is not included. but thanks for the solution.
-
Sebastien Lorber almost 10 yearsbased on your answer, I've made a proper function to handle this: stackoverflow.com/a/24168721/82609
-
tylerl over 9 yearsIf using this on a string that's shorter than the maxLength, the last word gets cut off. Maybe @AndrewJuniorHoward already stated the fix for this (
maxLength + 1
), but I fixed it by simply adding this line up top:var yourString += " ";
-
Scheintod over 9 yearsNote that indexOf can be replaced by lastIndexOf if hard boundaries are needed.
-
Shashank Agrawal over 8 years@Hamish your option works well, but it is including the last word also if the length exceeds. I tried altering the regex expression to exclude the last word if the max word limit is exceeding but didn't get it working. How can we achieve that?
-
JKesMc9tqIQe9M over 8 yearsI like this solution, but shouldn't the args passed to
$.extend
be reversed? -
loretoparisi about 8 years@Hamish look at this: supposed to have a var maxLength=140,text.length>=maxLength I have:
text.replace(/^(.{140}[^\s]*).*/, "$1").length
ok buttext.replace( new RegExp("/^(.{"+maxLength+"}[^\s]*).*/"), "$1").length
not, why? -
Ethan Chen almost 8 yearsThis doesn't seem to work if there are non-English characters towards the end of the string.
-
Andrey Gordeev almost 8 yearsUnfortunately, if you take away
fox jumps over the lazy dog
part, the result will beThe quick brown
, when it should beThe quick brown fox
. -
Chris Cinelli over 7 yearsThis always cuts the last word.
-
Tici over 7 yearsI completely forgot about the lastIndexOf(). Good catch!
-
naz786 about 7 yearsThis is a good solution, however if the purpose was to make text fit in a wide section, and there was 2 or 3 words in the string, if there was plenty of space and no overflow out of the section, this code would delete a whole word unnecessarily. So for the small string "Hello my name is Yusef", if it was in a wide div section: ["Hello my name is Yusef" ______] it will unsuccessfully change it to ["Hello my name is"______]. In my case, I wanted to keep whole words to fill a section.
-
Al-Mothafar almost 7 yearsWell, this is not really working correctly, sometimes I pass the maximum value as for example if the last word was 30 character already it will be with length more than 60 already! even if it set length to
{30}
-
Hamish almost 7 years@Al-Mothafar right well the question does ask "without cutting words" and I do say "plus any subsequent non-space characters" so it's working exactly as described. If you've got different needs you'll need to modify accordingly.
-
Al-Mothafar almost 7 yearsWell, in my opinion, the question does not specify, but make more sense with limitation to not exceed it anyway, so if he wants "this is a long string i cant display" or "this is a long string i can't", in your answer the result will show the last word "display" but in my case if limitation should be the second case.
-
Saumya Rastogi almost 7 years@NT3RP - According to your provided solution, the strings with the lesser length will not be truncated as desired.. Just for example please try the string - "Clever fox jumps over lazy dog", after giving any number to
maxLength
, the output according to your solution will be:Clever fox jumps over lazy
only, the last worddog
will be missed every time... -
Jeremy Daalder almost 7 yearsSo just wrap it in a test for the length of the string
if (yourString.length > maxLength)
.... -
Silvain over 6 yearsThis crashes if for some reason
str
isundefined
. I addedif (!str || str.length <= maxLen) return str;
-
hlozancic over 6 yearsOne of the better and smarter solutions out there for a simple problem like this :) Good job
-
hlozancic over 6 yearsBtw if you want to cut string like this which is multiline use this regex: ^([\s\S]{50}[^\s]*)[\s\S]*
-
shrewquest about 6 yearsthis doesn't handle the edge case where the separator doesn't occur in the string
-
Chris Cinelli about 6 years@shrewquest It works. If the separator is not in the string, it returns the string itself if
str.length <= maxLen
. Otherwise it returns an empty string. -
ChristoKiwi almost 6 yearsYou wrote a JS function that truncates a string to a number of words. Read the question again.
-
Mike Aron almost 6 yearseeehm. i think this is the only right answer to the question. He asked without cutting the word.
-
Shishir Arora almost 5 years@JeremyDaalder even then what if trimmedString ends with a space like "Clever fox jumps ".. better to trim() before returning
-
PSaul over 3 yearsThis doesn't account for the 'join(' ') characters, which can make the string longer than limit. If you change reduce()'s function to (acc, cur, idx) and the if to (acc + cur.length <= limit - idx) then it will account for the extra spaces when the words are joined back together. If being strictly within limit is required.
-
Bananaapple over 3 yearsThe trailing
.*
in the regex isn't necessary and could negatively impact performance. -
ggorlen almost 3 yearsThis potentially cuts off part of a word.
-
Niclas over 2 yearsThis returns empty string if str>maxLen. Solved it by adding if(str.split(separator)[0].length>maxLen) return str.split(separator)[0].substr(0,maxLen-2)+"...";
-
Conor Reid about 2 yearsThank you @hlozanic, This is what I was looking for and this should be appended to this top answer.
-
Wajid about 2 yearswithout the modification of @BeytanKurt this solution is not applicable.