4

I have a string as shown below which I want to truncate it after a particular character limit and display an ellipsis.

"This is a long string"

On truncation, I want the string to display something like this:

This is a long (...)

or

This is a (...)

or

This is (...)

The above examples don't cut the words. What I don't want is:

This is a lon (...)

or

This is a long s (...)

The above examples don't work.

I am using the following function in react to truncate a string. The problem with the function is that it sometimes cut the words. The value of length I am using is 175. For 175 character limit, sometimes it cut the words and sometimes it doesn't.

export const wordsTruncate = (words, length) => {
    const j = words.split('');
    let result = j.filter((z, a) => a <= (length - 1));

    return result.join("");
}

I am wondering what changes I need to make in the function above so that it doesn't cut the words as shown in the examples above.

10
  • What do you do if there's a massive word like 600000 random characters? That aside, it seems easier to use .slice() here -- words.slice(0, length).split(/\s+/), say? Your .split("") just splits on letters, not words. Commented May 24, 2021 at 2:03
  • @ggorlen thanks for the answer. I am wondering if you can explain me in an example. Commented May 24, 2021 at 2:07
  • I'm not sure about your application of this, but if you are displaying the text on a webpage. The CSS rules will handle this for you. Commented May 24, 2021 at 2:47
  • 2
    Does this answer your question? Shorten string without cutting words in JavaScript Commented May 24, 2021 at 2:56
  • @ggorlen I tried your answer but it didn't work. Commented May 27, 2021 at 16:49

7 Answers 7

1
+100

Try this. hope this works for you.

const wordsTruncate = (words, length) => {
  words = words.trim(); //you need to decied wheather you do this or not
  length -= 6; // because ' (...)'.length === 6
  if (length >= words.length) return words;

  let oldResult = /\s/.test(words.charAt(length));
  for (let i = length - 1; i > -1; i--) {
    const currentRusult = /\s/.test(words.charAt(i))

    //check if oldresult is white space and current is not.
    //which means current character is end of word
    if (oldResult && !currentRusult) {
      return `${words.substr(0, i + 1)} (...)`;
    }
    oldResult = currentRusult;
  }
  // you need to decide whether you will just return truncated or original
  // in case you want original just return word
  return '(...)';
}

console.log(wordsTruncate('This is long text blabla blasdf test.', 32));
console.log(wordsTruncate('This is', 22));
console.log(wordsTruncate('This is', 7));
console.log(wordsTruncate('This is', 13));
console.log(wordsTruncate('This is ', 13));
console.log(wordsTruncate('Thisadsfasdfasdfasdfasdf', 22));

So length should be final length of truncated string.

eg: 'This is long text blabla (...)'.length

Sign up to request clarification or add additional context in comments.

Comments

1

You might use a dynamic pattern, where you can specify a minimum number of chars at the start of the string for which you want the ellipsis to work, and the length of the number of characters.

^(?!\s)(.{3,10}\S)(?!\S).*

Regex demo

  • ^ Start ofs tring
  • (?!\s) Assert not a whitespace char at the start
  • (.{3,10}\S) Capture group 1 (Denoted by m[1] in the example code), match 3 - 10 times any char followed by matching a non whitespace char at the end
  • (?!\S) Negative lookahead, assert a whitespace boundary to the right
  • .* Match the rest of the line

const wordsTruncate = (s, minNrOfChars, length) => {
  if (minNrOfChars > length || minNrOfChars < 1 || length < 1) return s;
  minNrOfChars--;
  length--;
  const regex = new RegExp(`^(?!\\s)(.{${minNrOfChars},${length}\}\\S)(?!\\S).*`);
  const m = s.match(regex);
  return m ? `${m[1]} (...)` : s;
}

const strings = [
  "This is a long string",
  "a b c d e f g h",
  "a b c d e       ",
  "a b",
  "this isatest",
  "thisisatesttesttest",
  "A bcdefghifkkle",
  "a",
  "ab",
  "abc",
  "abcd",
  "abcde"
];

strings.forEach(s => {
  console.log(`"${s}" --> ${wordsTruncate(s, 3, 10)}`);
});

Comments

1

if you are using lodash in your react project, which most of us do, use it's truncate method like so

import truncate from 'lodash/truncate'

// ...

truncate('This is a long string', {
  'length': 20,
  'separator': ' ',
  'omission': ' ...',
})

// result: 'This is a long ...'

Comments

0

Try something like this:

export const wordsTruncate = (str, length) => {
  const words = str.split(' ');
  const result = words.reduce((acc, it) => {
    const parcial = acc + ' ' + it;
    return parcial.length >= length ? acc : parcial;
  }, '');

  return result + ' (...)';
};

Comments

0

[The value of length I am using is 175. For 175 character limit, sometimes it cut the words and sometimes it doesn't.]

You should check whether the 176th character is a space (" ") or not.

Comments

0

I hope this works for you.

function truncateWords(string, maxLen) {
    if (maxLen >= string.length) return string;
    if (string[maxLen - 6] == ' ') {
        return string.slice(0, maxLen - 5).split(' ').join(' ') + ' (...)';
    }
    return string.slice(0, maxLen - 5).split(' ').slice(0, -1).join(' ') + ' (...)';
}
const strings = [
  "abcdefghijklmno",
  "This is a long string",
  "a b c d e f g h",
  "a b c d e f g h i",
  "a b c d e       ",
  "a b",
  "this isatest",
  "thisisatesttesttest",
  "A bcdefghifkkle",
  "a",
  "ab",
  "abc",
  "abcd",
  "abcde"
];
strings.forEach(function(string) {
  console.log(truncateWords(string, 15));
});

/* result
abcdefghijklmno
This is a  (...)
a b c d e f g h
a b c d e  (...)
a b c d e  (...)
a b
this isatest
 (...)
A bcdefghifkkle
a
ab
abc
abcd
abcde
*/

Comments

0

Hope this works for you

const truncateWord = (string, length) => {
  if (string.length < length) {
    return `${string} (...)`;
  }
  return `${string.substring(0, string.substring(0, length).lastIndexOf(" "))} (...)`;
};

console.log(truncateWord("The quick brown fox jumps over the lazy dog", 10));


Result

The quick ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.