views:

84

answers:

3

I have a working solution right now, but it seems really ugly for something so (seemingly) simple.

I tried just breaking it when adding a word goes over the halfway mark, both splitting before and after adding the word, but depending on the length of the words it's either imbalanced towards the first or second line.

Sample inputs that I was initially having trouble with before the convoluted fix:

Input "Macaroni Cheese" and "Cheese Macaroni"

Should output "Macaroni<br/> Cheese" and "Cheese<br/> Macaroni" respectively.

But simpler solutions either worked on the first but not the second, or the other way around.

So here's what I have that works, but I'm wondering if there's a more elegant way to do this.

public string Get2LineDisplayText(string original)
{
    string[] words = original.Split(new[] {' ', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);

    //Degenerate case with only 1 word
    if (words.Length <= 1)
    {
        return original;
    }

    StringBuilder builder = new StringBuilder();
    builder.Append(words[0]); //Add first word without prepending space
    bool addedBr = false;
    foreach (string word in words.Skip(1))
    {
        if (builder.Length + word.Length < original.Length / 2) //Word fits on the first line without passing halfway mark
        {
            builder.Append(' ' + word);
        }
        else if (!addedBr) //Adding word goes over half, need to see if it's more balanced on the 1st or 2nd line
        {
            int diffOnLine1 = Math.Abs((builder.Length + word.Length) - (original.Length - builder.Length - word.Length));
            int diffOnLine2 = Math.Abs((builder.Length) - (original.Length - builder.Length));
            if (diffOnLine1 < diffOnLine2)
            {
                builder.Append(' ' + word);
                builder.Append("<br/>");
            }
            else
            {
                builder.Append("<br/>");
                builder.Append(' ' + word);
            }
            addedBr = true;
        }
        else //Past halfway and already added linebreak, just append
        {
            builder.Append(' ' + word);
        }
    }

    return builder.ToString();
}

Sample input/output:

+2  A: 
public static string Get2LineDisplayText(string original)
{
    //Degenerate case with only 1 word
    if (!original.Any(Char.IsWhiteSpace))
    {
        return original;
    }
    int mid = original.Length / 2;
    if (!Char.IsWhiteSpace(original[mid]))
    {
        for (int i = 1; i < mid; i += i)
        {
            if (Char.IsWhiteSpace(original[mid + i]))
            {
                mid = mid + i;
                break;
            }
            if (Char.IsWhiteSpace(original[mid - i]))
            {
                mid = mid - i;
                break;
            }
        }
    }

    return original.Substring(0, mid)
           + "<br />" + original.Substring(mid + 1);
}
James Curran
After understanding what the goal was, this is the sort of answer I would have come up with.
Jeff M
Doesn't compile but after fixing the typos and adding check for the case with a single word, it works. So essentially it's a breadth-first search for the index of the closest whitespace character to the middle. Clever.
Davy8
Hope you don't mind that I edited it with a version that compiles.
Davy8
+1  A: 

I tried my hand at it and arrived at :

String splitInMiddle(String s) {
    int middle = s.length() / 2;
    int right = s.indexOf(" ",middle);
    int left = s.lastIndexOf(" ",middle);

    int split = right;
    if ((right < 0) || (left + right > 2*middle)) {
        split = left;
    }
    return s.substring(0, split) + "<br/>\n" + s.substring(split + 1);
}

The principle is that it looks for the first space after and the last space before. If the left one is closer than the right one pick that.

Then glue the pieces I want with a CR instead of a space.

Peter Tillemans
+1  A: 

Here's what I came up with:

    public static string Get2Lines(string input)
    {
        //Degenerate case with only 1 word
        if (input.IndexOf(' ') == -1)
        {
            return input;
        }
        int mid = input.Length / 2;

        int first_index_after = input.Substring(mid).IndexOf(' ') + mid;
        int first_index_before = input.Substring(0, mid).LastIndexOf(' ');

        if (first_index_after - mid < mid - first_index_before)
            return input.Insert(first_index_after, "<BR />");
        else
            return input.Insert(first_index_before, "<BR />");
    }
Matt Brunell
Added the check for just a single word.
Davy8
Oh yeah, good call.
Matt Brunell