JavaScriptで単語をカットせずに文字列を短くする


102

私はJavaScriptでの文字列操作があまり得意ではないので、単語を切り取らずに文字列を短くするにはどうすればよいのかと考えていました。私はsubstringの使い方を知っていますが、indexOfなどはあまりよくわかりません。

次の文字列があるとします。

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

10文字に減らしたいのですが、スペースで終わっていない場合は単語を完成させます。文字列変数を次のようにしたくありません。

「これは私が表示できない長い文字列です」

スペースができるまで単語を完成させたい。


あなたはひもをトリムすることを意味しますか?試してみる" too many spaces ".trim()
アヌラーク

1
いくつかの入力例と予想される出力は、この質問に答えるのに大いに役立ちます。
だます

申し訳ありませんが、テキスト=「これは表示できない長い文字列です。これは「これは私が表示できない長い文字列です」
Josh Bedo '28

回答:


180

私が正しく理解している場合は、文字列を特定の長さに(たとえば"The quick brown fox jumps over the lazy dog"、単語を切り取らずに6文字に)短くしたいとします。

この場合、次のようなことを試すことができます。

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 ".replace"が "jQuery functions"で機能しないことは絶対に正しくありません。「jQuery関数」などさえありません。
とがった

3
それは「maxLength + 1」であるべきではありません。また、maxLengthが完全な文の長さ以上の場合、最後の単語は含まれません。しかし、解決策をありがとう。
Beytan Kurt 2014

4
maxLengthより短い文字列でこれを使用すると、最後の単語が切り捨てられます。たぶん@AndrewJuniorHowardはこれの修正(maxLength + 1)をすでに述べましたが、私はこの行を上に追加するだけで修正しました:var yourString += " ";
tylerl

3
残念ながら、fox jumps over the lazy dog一部を削除するとThe quick brown 、結果はになりますThe quick brown fox
Andrey Gordeev

2
これは常に最後の単語をカットします。
Chris Cinelli、

108

これを行う方法はたくさんありますが、正規表現は便利な1行の方法です。

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

この式は、最初の11文字(任意)とそれに続く空白以外の文字を返します。

スクリプトの例:

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

出力:

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

素晴らしい、私は文字通りこの質問を100万通りグーグル検索し、これに近いものやループを含むPHPの作業バージョンを見つけることしかできませんでした。
Josh Bedo、2011年

1
これは、最初の(この場合のみ)部分式の一致(大括弧内のもの)を参照しています。$ 0は一致全体を指します。この場合は文字列全体です。
ハミッシュ

3
:@joshあなたは正規表現オブジェクトを使用して、変数maxの長さを作ることができるはずt.replace(new RegExp("^(.{"+length+"}[^\s]*).*"), "$1")
rjmackayを

1
@Hamishオプションはうまく機能しますが、長さが長すぎる場合も最後の単語が含まれます。正規表現を変更して、最大単語数の制限を超えていても機能しない場合は、最後の単語を除外してみました。どうすればそれを達成できますか?
Shashank Agrawal 2015

1
まあ、これは実際には正しく機能していません。たとえば、最後の単語がすでに30文字だった場合など、最大値を渡すこともあります。それが長さに設定されている場合でも{30}
Al-Mothafar 2017

65

このような単純な問題の場合、読みづらい回答が多数あり、選択したものを含めて一部が機能しないことに、私はちょっと驚いています。

通常、結果の文字列は最大で 文字にしたいと思いmaxLenます。この同じ関数を使用して、URLのスラッグを短縮します。

str.lastIndexOf(searchValue[, fromIndex]) 文字列の後方検索を開始するインデックスである2番目のパラメーターを受け取り、物事を効率的かつ簡単にします。

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

これはサンプル出力です:

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"

そしてナメクジ:

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
私はlastIndexOf()について完全に忘れていました。良いキャッチ!
Tici

2
なんらかの理由でstrがクラッシュした場合undefined。追加しましたif (!str || str.length <= maxLen) return str;
Silvain 2017

これは、区切り文字が文字列で発生しないエッジケースを処理しません
shrewquest

@shrewquest動作します。区切り文字が文字列にない場合は、文字列自体を返しますstr.length <= maxLen。それ以外の場合は、空の文字列を返します。
Chris Cinelli、2018

20

indexOfが2つの引数、つまり照合する文字列と文字インデックスから始まることを誰もが忘れているようです。10文字の後の最初のスペースで文字列を分割できます。

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

ハード境界が必要な場合は、indexOfをlastIndexOfに置き換えることができます。
Scheintod 2014年

14

Lodashはこれのために特別に書かれた関数を持っています: _.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

いくつかのコーナーケースを処理しないNT3RP回答に基づいて、このコードを作成しました。サイズ> maxLengthイベントのテキストが返されないことが保証され、省略記号...が最後に追加されました。

これは、単一の単語が> 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;
}

これが気になる場合は、jqueryの依存関係を簡単に削除できると思います。


3
私はこの解決策が好きですが、渡された引数を$.extend逆にしてはいけませんか?
JKesMc9tqIQe9M 2015年

5

これが1行のソリューションです。

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

私はパーティーに遅れましたが、これは、大量の単語を返すために思いついた小さくて簡単な解決策です。

キャラクターの要件とは直接関係ありませんが、あなたが望んでいたと同じ結果もたらします。

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, '...'));

こちらの実際の例をご覧くださいhttps : //jsfiddle.net/bx7rojgL/


文字列をいくつかの単語に切り捨てるJS関数を作成しました。もう一度質問を読んでください。
ChristoKiwi

1
ええ。これが質問に対する唯一の正しい答えだと思います。彼は言葉を切らずに尋ねました。
マイクアロン2018

2

これは、最終的な単語を含めるのではなく除外します。

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

使用法:

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

2

私は別のアプローチをとりました。同様の結果が必要でしたが、戻り値を指定した長さよりも短くしたいと考えました。

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

編集ここで少しリファクタリングしました: JSFiddle Example。連結する代わりに、元の配列に再結合します。

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

truncate以下のワンライナーを使用できます。

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 = "これは長い文字列であり、すべてを説明することはできません"; 短縮(s、10、 '...')

/* "これは .." */


1

次に、句読点に沿って切り捨てる別のコードを示します(これを探していて、Googleがこの質問をここで見つけました)。自分で解決策を考え出さなければならなかったので、これは15分でハッキングしたものです。のすべての出現を検索します。!?そして、これらの<よりも大きい任意の位置で切り捨てますlen

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

Typescript、および省略記号:)

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

これは、文字列の最後に句読点や空白を残さずに単語の境界に切り詰めるためにこれを書いたものです。

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

投票されたソリューションは満足できるものではありませんでした。だから私はそれが一般的なものであり、あなたのテキストの最初と最後の部分の両方で機能するものを書きました(substrのようなものですが、単語のためのものです)。また、char-countでスペースを除外するかどうかを設定できます。

    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

私はこれに遅れましたが、この関数は正確にOPが要求するものを作ると思います。SENTENCE値とLIMIT値を簡単に変更して、さまざまな結果を得ることができます。

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

このスニペットの出力は、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

空の文や非常に長い最初の単語などの境界条件を使用します。また、言語固有の文字列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

「トマトとほうれん草のパスタ」

単語を半分にしたくない場合

最初の反復:

acc:0 / acc + cur.length = 5 / newTitle = ['パスタ'];

2回目の反復:

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

3回目の反復:

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

4回目の反復:

acc:15 / acc + cur.length = 18(制限範囲)/ 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(' ')} ...`
}

出力:トマトのパスタ...


-1

これでスペースをトリムできます:

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

-1

@ NT3RPから更新文字列が最初にスペースにヒットした場合、その単語が削除されて文字列が1単語短くなることがわかりました。したがって、maxLengthがスペースに落ちないことを確認するために、if elseステートメントを投入しました。

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.