3

I am trying to chop text to a fixed length, but I don't want to chop words at half, so I need to do something like this:

function fixedLength(str, len, bol) {
    var i, l = str.length, left = 0, right = l - 1, rtn, tmp;

    tmp = str.charAt(len);

    if (bol || tmp === " " || tmp === "") {
        rtn = str.substr(0, len);
    } else {
        tmp = len - 1;

        for (i = tmp; i > -1; i--) {
            if (str.charAt(i) === " ") {
                left = i;
                break;
            }
        }

        for (i = tmp; i < l; i++) {
            if (str.charAt(i) === " ") {
                right = i;
                break;
            }
        }

        rtn = str.substr(0, ((Math.abs(left - tmp) <= Math.abs(right - tmp)) ? left : right));
    }

    return rtn + "...";
}

But when I use it with this:

var str = "the quick brown fox jumped over the lazy dog";

for (var i = 0; i < 45; i++) {
    document.write("i:" + i + " - " + fixedLength(str, i) + "<br>");
}

Everyone seems to work correctly except in this line "i:43 - the quick brown fox jumped over the lazy do...", the word "dog" is chopped in half (Demo)

I can't find the flaw, every time I change something, I add a new bugs

Gumbo
  • 643,351
  • 109
  • 780
  • 844
ajax333221
  • 11,436
  • 16
  • 61
  • 95
  • 5
    Well for one, the cat in your gravatar looks awfully suspicious... – tskuzzy Jun 24 '12 at 05:25
  • 3
    For those too lazy to check the edit history, the original title was: "Why was my dog chopped in half :(?" It was an epic title. I am sad to see it has been edited. :-( – Platinum Azure Jun 24 '12 at 05:29
  • Is this any help: http://jsfiddle.net/YXwfb/1/ ? Basically, this looks for the last word boundary in `len` characters. – Kobi Jun 24 '12 at 05:47

5 Answers5

4

Didn't check your code, but you could write your code more simple:

function fixedLength(str, len, bol) {
    while(!bol && str[len] && str[len] !== ' ') {
      len--;
    }
    return str.substr(0, len) + '...';
}

And the demo.

xdazz
  • 158,678
  • 38
  • 247
  • 274
  • 1
    well, my code chopped the word depending on the position, if it was here `abXcde` it was not included, but `abcXde` it was, etc etc. But I think this feature is stupid and I will simply use your solution. Although my question was to find the source of error, I think a solution that makes it unnecessary to fix the error should also be candidate to be accepted, yeah... I am accepting this, thanks – ajax333221 Jun 24 '12 at 05:58
  • @ajax333221 Yep, one function to do one thing, KISS :) – xdazz Jun 24 '12 at 06:00
1

You could try to use regex to find the last space character.

function fixedLength(str, len, bol) {
    if(str.length <= len) {
       return str;  
    }

    var rtn = str.substr(0, len).match(/.* /);
    if(rtn == null) {
        rtn = "";
    } else {
        rtn = rtn + "...";
    }
    return rtn;
}

Demo here: http://jsfiddle.net/R8qMQ/2/

I also added a verification, if the input string is already in the max allowed length, simply return it. If there are no words that can be split, return an empty string instead of NULL.

I would go with regex since I could also add other characters in the future that I might consider as word delimiters (e.g. . or ; or maybe REGEX b - word delimiter).

Stelian Matei
  • 11,553
  • 2
  • 25
  • 29
1

In case you're interested, the bug in your original code was in the final assignment to the return value, where you compared tmp - left and tmp - right. The problem is that in the case of the last word in the string, "dog", right never gets re-assigned after its initial value is set to l - 1; and so your algorithm acts as if a space were found at index 44 when in fact there is a g there.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
0
rtn = str.substr(0, ((Math.abs(left - tmp + 1) <= Math.abs(right - tmp)) ? left : right));

I didn't debug it with a debugger but using your Demo was very helpful.

Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
0

I tried refactoring your code in a very understandable manner:

function prettyCut(word, length) {
    //account for ellipses
    length -= 3;

    //break down into lengths
    var units = word.split(" ").map(function(word) {
        return word.length+1;
    });

    //map to accumulated sums
    var runningSum = 0;
    var sums = units.map(function(length, index) {
        return (runningSum += length)
    }); 

    //find difference from goal of each sum
    var differences = sums.map(function(sum) {
        return (length-sum)>0?(length-sum):100;
    });

    //minimize and return
    var closest = Math.min.apply(this, differences);
    return closest==0?word:word.substr(0, length-closest)+"...";
}
prettyCut("the quick brown fox jumped over the lazy dog", 45);
matt3141
  • 4,303
  • 1
  • 19
  • 24