Accorcia la stringa senza tagliare le parole in JavaScript


102

Non sono molto bravo con la manipolazione delle stringhe in JavaScript e mi chiedevo come faresti ad accorciare una stringa senza tagliare nessuna parola. So come usare la sottostringa, ma non indexOf o qualcosa di veramente buono.

Diciamo che ho avuto la seguente stringa:

text = "this is a long string I cant display"

Voglio ridurlo a 10 caratteri, ma se non finisce con uno spazio, finisci la parola. Non voglio che la variabile stringa assomigli a questa:

"questa è una lunga stringa che non posso ignorare"

Voglio che finisca la parola finché non compare uno spazio.


intendi tagliare una corda? prova" too many spaces ".trim()
Anurag

1
Alcuni input di esempio e output attesi aiuterebbero molto a rispondere a questa domanda.
inganno

va bene scusa dico che avevo la stringa text = "questaèuna stringa lunga che non posso mostrare" voglio ridurla a 10 caratteri ma se non finisce con uno spazio finire la parola non voglio che la variabile stringa assomigli this "this is a long string I cant dis"
Josh Bedo

Risposte:


180

Se ho capito bene, vuoi accorciare una stringa a una certa lunghezza (ad esempio accorciarla "The quick brown fox jumps over the lazy dog"a, diciamo, 6 caratteri senza tagliare nessuna parola).

In questo caso, puoi provare qualcosa di simile a quanto segue:

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 and re-trim only when necessary (prevent re-trim when string is shorted than maxLength, it causes last word cut) 
if(yourString.length > trimmedString.length){
    //trim the string to the maximum length
    var trimmedString = yourString.substr(0, maxLength);

    //re-trim if we are in the middle of a word and 
    trimmedString = trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(" ")))
}

9
@ josh non è assolutamente vero che ".replace" non funziona in "jQuery functions". Non esiste nemmeno una cosa come una "funzione jQuery".
Pointy

3
non dovrebbe essere "maxLength + 1". E se maxLength è maggiore o uguale alla lunghezza completa della frase, l'ultima parola non viene inclusa. ma grazie per la soluzione.
Beytan Kurt,

4
Se lo usi su una stringa più corta di maxLength, l'ultima parola viene tagliata. Forse @AndrewJuniorHoward ha già indicato la correzione per questo ( maxLength + 1), ma l'ho risolto semplicemente aggiungendo questa riga in alto:var yourString += " ";
tylerl

3
Sfortunatamente, se togli una fox jumps over the lazy dogparte, il risultato sarà The quick brown , quando dovrebbe essere The quick brown fox.
Andrey Gordeev

2
Questo taglia sempre l'ultima parola.
Chris Cinelli

108

Ci sono molti modi per farlo, ma un'espressione regolare è un utile metodo di una riga:

"this is a longish string of text".replace(/^(.{11}[^\s]*).*/, "$1"); 
//"this is a longish"

Questa espressione restituisce i primi 11 caratteri (qualsiasi) più eventuali caratteri successivi non spazi.

Script di esempio:

<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>

Produzione:

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

Fantastico, ho letteralmente cercato su Google questa domanda in un milione di modi e sono riuscito a trovare solo una versione funzionante per php niente vicino a questo e coinvolgente loop.
Josh Bedo

1
Si riferisce alla prima (e unica, in questo caso) corrispondenza di sottoespressione: le cose tra parentesi. $ 0 si riferirebbe all'intera corrispondenza, che in questo caso è l'intera stringa.
Hamish

3
@josh Dovresti essere in grado di rendere la lunghezza massima una variabile usando un oggetto regexp:t.replace(new RegExp("^(.{"+length+"}[^\s]*).*"), "$1")
rjmackay

1
@Hamish la tua opzione funziona bene, ma include l'ultima parola anche se la lunghezza supera. Ho provato ad alterare l'espressione regex per escludere l'ultima parola se il limite massimo di parole è superiore, ma non l'ho fatto funzionare. Come possiamo ottenerlo?
Shashank Agrawal

1
Bene, questo non funziona proprio correttamente, a volte passo il valore massimo come ad esempio se l'ultima parola era già di 30 caratteri, sarà già con una lunghezza superiore a 60! anche se ha impostato la durata su{30}
Al-Mothafar

65

Sono un po 'sorpreso che per un problema semplice come questo ci siano così tante risposte difficili da leggere e alcune, inclusa quella prescelta, non funzionano.

Di solito voglio che la stringa del risultato sia composta al massimo da maxLen caratteri. Uso anche questa stessa funzione per abbreviare gli slug negli URL.

str.lastIndexOf(searchValue[, fromIndex]) prende un secondo parametro che è l'indice dal quale iniziare la ricerca all'indietro nella stringa rendendo le cose semplici ed efficienti.

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

Questo è un output di esempio:

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"

E per la lumaca:

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"

1
Mi sono completamente dimenticato di lastIndexOf (). Buona pesca!
Tici

2
Questo va in crash se per qualche motivo lo strè undefined. Ho aggiuntoif (!str || str.length <= maxLen) return str;
Silvain

questo non gestisce il caso limite in cui il separatore non si trova nella stringa
scaltro

@shrewquest Funziona. Se il separatore non è nella stringa, restituisce la stringa stessa se str.length <= maxLen. Altrimenti restituisce una stringa vuota.
Chris Cinelli

20

Tutti sembrano dimenticare che indexOf richiede due argomenti: la stringa da trovare e l'indice dei caratteri da cui iniziare a cercare. Puoi spezzare la stringa nel primo spazio dopo 10 caratteri.

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
*/

Nota che indexOf può essere sostituito da lastIndexOf se sono necessari limiti rigidi.
Scheintod

14

Lodash ha una funzione scritta appositamente per questo: _.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...'

7

Sulla base della risposta NT3RP che non gestisce alcuni casi d'angolo, ho creato questo codice. Garantisce di non restituire un testo con un evento size> maxLength, ...alla fine è stata aggiunta un'ellissi .

Questo gestisce anche alcuni casi d'angolo come un testo che ha una sola parola> maxLength

shorten: function(text,maxLength,options) {
    if ( text.length <= maxLength ) {
        return text;
    }
    if ( !options ) options = {};
    var defaultOptions = {
        // By default we add an ellipsis at the end
        suffix: true,
        suffixString: " ...",
        // By default we preserve word boundaries
        preserveWordBoundaries: true,
        wordSeparator: " "
    };
    $.extend(options, defaultOptions);
    // Compute suffix to use (eventually add an ellipsis)
    var suffix = "";
    if ( text.length > maxLength && options.suffix) {
        suffix = options.suffixString;
    }

    // Compute the index at which we have to cut the text
    var maxTextLength = maxLength - suffix.length;
    var cutIndex;
    if ( options.preserveWordBoundaries ) {
        // We use +1 because the extra char is either a space or will be cut anyway
        // This permits to avoid removing an extra word when there's a space at the maxTextLength index
        var lastWordSeparatorIndex = text.lastIndexOf(options.wordSeparator, maxTextLength+1);
        // We include 0 because if have a "very long first word" (size > maxLength), we still don't want to cut it
        // But just display "...". But in this case the user should probably use preserveWordBoundaries:false...
        cutIndex = lastWordSeparatorIndex > 0 ? lastWordSeparatorIndex : maxTextLength;
    } else {
        cutIndex = maxTextLength;
    }

    var newText = text.substr(0,cutIndex);
    return newText + suffix;
}

Immagino che tu possa rimuovere facilmente la dipendenza jquery se questo ti dà fastidio.


3
Mi piace questa soluzione, ma gli argomenti passati non dovrebbero $.extendessere invertiti?
JKesMc9tqIQe9M

6

Ecco una soluzione in una riga.

text = "this is a long string I cant display"

function shorten(text,max) {
    return text && text.length > max ? text.slice(0,max).split(' ').slice(0, -1).join(' ') : text
}


console.log(shorten(text,10));


3

Sono in ritardo alla festa, ma ecco una piccola e facile soluzione che mi è venuta per restituire un po 'di parole.

Non è direttamente correlato alle tue esigenze di personaggi , ma ha lo stesso risultato che credo tu stia cercando.

function truncateWords(sentence, amount, tail) {
  const words = sentence.split(' ');

  if (amount >= words.length) {
    return sentence;
  }

  const truncated = words.slice(0, amount);
  return `${truncated.join(' ')}${tail}`;
}

const sentence = 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.';

console.log(truncateWords(sentence, 10, '...'));

Guarda l'esempio di lavoro qui: https://jsfiddle.net/bx7rojgL/


Hai scritto una funzione JS che tronca una stringa in un numero di parole. Leggi di nuovo la domanda.
ChristoKiwi

1
eeehm. Penso che questa sia l'unica risposta giusta alla domanda. Ha chiesto senza tagliare la parola.
Mike Aron

2

Questo esclude l'ultima parola invece di includerla.

function smartTrim(str, length, delim, appendix) {
    if (str.length <= length) return str;

    var trimmedStr = str.substr(0, length+delim.length);

    var lastDelimIndex = trimmedStr.lastIndexOf(delim);
    if (lastDelimIndex >= 0) trimmedStr = trimmedStr.substr(0, lastDelimIndex);

    if (trimmedStr) trimmedStr += appendix;
    return trimmedStr;
}

Utilizzo:

smartTrim(yourString, 11, ' ', ' ...')
"The quick ..."

2

Ho adottato un approccio diverso. Anche se avevo bisogno di un risultato simile, volevo mantenere il mio valore di ritorno inferiore alla lunghezza specificata.

function wordTrim(value, length, overflowSuffix) {
    value = value.trim();
    if (value.length <= length) return value;
    var strAry = value.split(' ');
    var retString = strAry[0];
    for (var i = 1; i < strAry.length; i++) {
        if (retString.length >= length || retString.length + strAry[i].length + 1 > length) break;
        retString += " " + strAry[i];
    }
    return retString + (overflowSuffix || '');
}

Modifica L' ho refactoring un po 'qui: JSFiddle Example . Si ricongiunge all'array originale invece di concatenarsi.

function wordTrim(value, length, overflowSuffix) {
    if (value.length <= length) return value;
    var strAry = value.split(' ');
    var retLen = strAry[0].length;
    for (var i = 1; i < strAry.length; i++) {
        if(retLen == length || retLen + strAry[i].length + 1 > length) break;
        retLen+= strAry[i].length + 1
    }
    return strAry.slice(0,i).join(' ') + (overflowSuffix || '');
}

2
function shorten(str,n) {
  return (str.match(RegExp(".{"+n+"}\\S*"))||[str])[0];
}

shorten("Hello World", 3); // "Hello"


1

Puoi usare truncateuna riga di seguito:

const text = "The string that I want to truncate!";

const truncate = (str, len) => str.substring(0, (str + ' ').lastIndexOf(' ', len));

console.log(truncate(text, 14));


1
shorten(str, maxLen, appendix, separator = ' ') {
if (str.length <= maxLen) return str;
let strNope = str.substr(0, str.lastIndexOf(separator, maxLen));
return (strNope += appendix);

}

var s = "questa è una stringa lunga e non posso spiegare tutto"; accorciare (s, 10, '...')

/* "questo è .." */


1

Ecco ancora un altro pezzo di codice che tronca lungo i segni di punteggiatura (stavo cercando questo e Google ha trovato questa domanda qui). Ho dovuto trovare una soluzione da solo, quindi questo è ciò che ho hackerato in 15 minuti. Trova tutte le occorrenze di. ! ? e tronca in qualsiasi posizione di questi che è <dilen

function pos(str, char) {
    let pos = 0
    const ret = []
    while ( (pos = str.indexOf(char, pos + 1)) != -1) {
        ret.push(pos)
    }
    return ret
}

function truncate(str, len) {
    if (str.length < len)
        return str

    const allPos = [  ...pos(str, '!'), ...pos(str, '.'), ...pos(str, '?')].sort( (a,b) => a-b )
    if (allPos.length === 0) {
        return str.substr(0, len)
    }

    for(let i = 0; i < allPos.length; i++) {
        if (allPos[i] > len) {
            return str.substr(0, allPos[i-1] + 1)
        }
    }
}

module.exports = truncate

1

Dattiloscritto e con i puntini di sospensione :)

export const sliceByWord = (phrase: string, length: number, skipEllipses?: boolean): string => {
  if (phrase.length < length) return phrase
  else {
    let trimmed = phrase.slice(0, length)
    trimmed = trimmed.slice(0, Math.min(trimmed.length, trimmed.lastIndexOf(' ')))
    return skipEllipses ? trimmed : trimmed + '…'
  }
}

0

Per quel che vale, ho scritto questo per troncare al confine della parola senza lasciare punteggiatura o spazi bianchi alla fine della stringa:

function truncateStringToWord(str, length, addEllipsis)
{
    if(str.length <= length)
    {
        // provided string already short enough
        return(str);
    }

    // cut string down but keep 1 extra character so we can check if a non-word character exists beyond the boundary
    str = str.substr(0, length+1);

    // cut any non-whitespace characters off the end of the string
    if (/[^\s]+$/.test(str))
    {
        str = str.replace(/[^\s]+$/, "");
    }

    // cut any remaining non-word characters
    str = str.replace(/[^\w]+$/, "");

    var ellipsis = addEllipsis && str.length > 0 ? '&hellip;' : '';

    return(str + ellipsis);
}

var testString = "hi stack overflow, how are you? Spare";
var i = testString.length;

document.write('<strong>Without ellipsis:</strong><br>');

while(i > 0)
{
  document.write(i+': "'+ truncateStringToWord(testString, i) +'"<br>');
  i--;
}

document.write('<strong>With ellipsis:</strong><br>');

i = testString.length;
while(i > 0)
{
  document.write(i+': "'+ truncateStringToWord(testString, i, true) +'"<br>');
  i--;
}


0

Le soluzioni votate non sono state soddisfacenti. Quindi ho scritto qualcosa che è un po 'generico e funziona sia la prima che l'ultima parte del tuo testo (qualcosa come substr ma per le parole). Inoltre puoi impostare se desideri che gli spazi vengano omessi nel conteggio dei caratteri.

    function chopTxtMinMax(txt, firstChar, lastChar=0){
        var wordsArr = txt.split(" ");
        var newWordsArr = [];

        var totalIteratedChars = 0;
        var inclSpacesCount = true;

        for(var wordIndx in wordsArr){
            totalIteratedChars += wordsArr[wordIndx].length + (inclSpacesCount ? 1 : 0);
            if(totalIteratedChars >= firstChar && (totalIteratedChars <= lastChar || lastChar==0)){
                newWordsArr.push(wordsArr[wordIndx]);
            }
        }

        txt = newWordsArr.join(" ");
        return txt;
    }

0

Sono arrivato in ritardo per questo, ma penso che questa funzione faccia esattamente ciò che richiede OP. Puoi facilmente modificare i valori SENTENCE e LIMIT per risultati diversi.

function breakSentence(word, limit) {
  const queue = word.split(' ');
  const list = [];

  while (queue.length) {
    const word = queue.shift();

    if (word.length >= limit) {
      list.push(word)
    }
    else {
      let words = word;

      while (true) {
        if (!queue.length ||
            words.length > limit ||
            words.length + queue[0].length + 1 > limit) {
          break;
        }

        words += ' ' + queue.shift();
      }

      list.push(words);
    }
  }

  return list;
}

const SENTENCE = 'the quick brown fox jumped over the lazy dog';
const LIMIT = 11;

// get result
const words = breakSentence(SENTENCE, LIMIT);

// transform the string so the result is easier to understand
const wordsWithLengths = words.map((item) => {
  return `[${item}] has a length of - ${item.length}`;
});

console.log(wordsWithLengths);

L'output di questo snippet è dove il LIMIT è 11 è:

[ '[the quick] has a length of - 9',
  '[brown fox] has a length of - 9',
  '[jumped over] has a length of - 11',
  '[the lazy] has a length of - 8',
  '[dog] has a length of - 3' ]

0

Con condizioni al contorno come una frase vuota e una prima parola molto lunga. Inoltre, non utilizza alcuna stringa specifica per la lingua api / library.

function solution(message, k) {
    if(!message){
        return ""; //when message is empty
    }
    const messageWords = message.split(" ");
    let result = messageWords[0];
    if(result.length>k){
        return ""; //when length of first word itself is greater that k
    }
    for(let i = 1; i<messageWords.length; i++){
        let next = result + " " + messageWords[i];

        if(next.length<=k){
            result = next;
        }else{
            break;
        }
    }
    return result;
}

console.log(solution("this is a long string i cant display", 10));


0

'Pasta con pomodoro e spinaci'

se non vuoi tagliare la parola a metà

prima iterazione:

acc: 0 / acc + cur.length = 5 / newTitle = ['Pasta'];

seconda iterazione:

acc: 5 / acc + cur.length = 9 / newTitle = ['Pasta', 'with'];

terza iterazione:

acc: 9 / acc + cur.length = 15 / newTitle = ['Pasta', 'with', 'tomato'];

quarta iterazione:

acc: 15 / acc + cur.length = 18 (limite limite) / newTitle = ['Pasta', 'with', 'tomato'];

const limitRecipeTitle = (title, limit=17)=>{
    const newTitle = [];
    if(title.length>limit){
        title.split(' ').reduce((acc, cur)=>{
            if(acc+cur.length <= limit){
                newTitle.push(cur);
            }
            return acc+cur.length;
        },0);
    }

    return `${newTitle.join(' ')} ...`
}

output: Pasta al pomodoro ...


-1

Puoi tagliare gli spazi con questo:

var trimmedString = flabbyString.replace(/^\s*(.*)\s*$/, '$1');

-1

Aggiornato da @ NT3RP Ho scoperto che se la stringa colpisce uno spazio per la prima volta intorno ad essa, finirà per eliminare quella parola rendendo la tua stringa una parola più corta di quanto possa essere. Quindi ho appena inserito un'istruzione if else per verificare che maxLength non cada su uno spazio.

codepen.io

var yourString = "The quick brown fox jumps over the lazy dog"; //replace with your string.
var maxLength = 15 // maximum number of characters to extract

if (yourString[maxLength] !== " ") {

//trim the string to the maximum length
var trimmedString = yourString.substr(0, maxLength);

alert(trimmedString)

//re-trim if we are in the middle of a word
trimmedString = trimmedString.substr(0, Math.min(trimmedString.length, trimmedString.lastIndexOf(" ")))
}

else {
  var trimmedString = yourString.substr(0, maxLength);
}

alert(trimmedString)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.