Justify a sentence

Did a code sample for a company in Seattle, they asked me to write code to justify a sentence to a specific length. Besides the fact that the HR rep had no idea how to do anything concerning computers, I think we finally managed to get the code uploaded to the team for review.

They said I had no experience in coding whatsoever, so I'm not really sure what's screwed up in the code below. To properly add the spaces, we'd have to know the number of words. So first pass counts the words, second pass inserts the spaces. I could have used a StringBuilder I suppose, but I didn't see a need for that overhead. It is ascii based as per their requirements.

If you can see what I screwed up, let me know!

Back to Code Samples

/// <summary>
/// Adapts a string to fit across a specified width. Whitespace is added between words to
/// make the string the correct width.
/// </summary>
/// <param name="source">The string to justify.</param>
/// <param name="width">The justified width.</param>
/// <returns>The string adapted to with specified width.</returns>
public static string Justify(this string source, int width)
{
const char whitespaceChar = ' ';

// Short circuit if no work to do.
if (source == null)
{
return null;
}

if (width < source.Length)
{
throw new ArgumentException(nameof(width), "Justification width must be >= source string length.");
}

// First pass through the string, find the total number of words. We need this to know how many characters
// to insert between non-whitespace clusters.
int nonWhitespaceClusterCount = source.GetNonWhitespaceClusterCount();

// Short circuit for single word.
if (nonWhitespaceClusterCount <= 1)
{
return source.PadRight(width);
}

int whitespaceCharsToInsertPerGap = (width - source.Length) / (nonWhitespaceClusterCount - 1);
var remainingWhitespaceCharsToDistribute = (width - source.Length) % (nonWhitespaceClusterCount - 1);

// Create the destination, pre-allocate since we know how big this will be.
var justifiedString = new char[width];
int justifiedStringOffset = 0;

var inWhitespaceGap = false;

foreach (var currentCharacter in source)
{
justifiedString[justifiedStringOffset++] = currentCharacter;

var currentCharIsWhitespace = char.IsWhiteSpace(currentCharacter);

if (!inWhitespaceGap && currentCharIsWhitespace)
{
for(var i = 0; i < whitespaceCharsToInsertPerGap; i++)
{
justifiedString[justifiedStringOffset++] = whitespaceChar;
}

// Don't like this. All extra spaces are added from left to right.
// TODO: Distribute the spaces a little better. Each end? Start in middle? All at end? Talk to the PM, see what they want done.
if (remainingWhitespaceCharsToDistribute-- > 0)
{
justifiedString[justifiedStringOffset++] = whitespaceChar;
}
}

inWhitespaceGap = currentCharIsWhitespace;
}

return new string(justifiedString);
}

/// <summary>
/// In order to justify a string we need the number of non-whitespace character groups between which we'll
/// add the extra whitespace.
/// </summary>
/// <param name="source">The string to analyze</param>
/// <returns>The number of non-whitespace clusters.</returns>
private static int GetNonWhitespaceClusterCount(this string source)
{
int nonWhitespaceClusterCount = 0;

if (source != null)
{
bool inWord = false;

foreach (var currentChar in source)
{
if (char.IsWhiteSpace(currentChar))
{
inWord = false;
}
else if (!inWord)
{
nonWhitespaceClusterCount++;
inWord = true;
}
}
}

return nonWhitespaceClusterCount;
}

Back to Code Samples