Wednesday, 27 May 2009

Silverlight Utility to decode Roman Numerals and spell out numbers

Utility to decode roman numerals and spell out numbers I’ve had another chance to play around with Silverlight 2 over the last two days. The result: a new version of my utility to convert numbers to words. I’ve now added the ability to convert to and from Roman Numerals, making it substantially more useful – if you’re a Roman citizen caught in a time warp. Go try it.

I love the way Silverlight lets you run standard C# in the browser – generics, LINQ and all. The code for the conversion algorithms comes straight from two of my Project Euler posts, Converting Numbers to Words and Converting to and from Roman Numerals. From my limited experiment, the Model-View-View Model pattern also appears to work pretty much as it does in WPF, though data binding in Silverlight is not so sophisticated. For example, there is no UpdateSourceTrigger mode in Silverlight, so when you want a TextBox to update its data source every time the text changes you have to resort to a hack (switching focus away from the TextBox, then back again).

Unit Testing Silverlight

I’ve become quite a fan of unit testing over the last 9 months or so of my latest project, and I wanted to make sure we’ll be able to carry on the practice in Silverlight. Turns out it can be done, but choices are more limited than on the full CLR – mostly due to Silverlight only running in the browser (unless you are devious). There’s SilverUnit, but that’s built on top of Typemock Isolator which commercial users have to pay for. Or there’s Silverlight Unit Testing framework from Microsoft, which you can get free as part of the Silverlight Toolkit on Codeplex. I went with the free option.

Creating tests with Silverlight Unit Testing Framework is straightforward - you just decorate your classes and test methods with attributes, just as with other Unit Testing frameworks. At the moment though, options for running the tests are limited: you pretty much have to use the provided method, that runs through all the tests in a browser window. My colleague is currently trying out a technique using a powershell script to automate Internet Explorer so that we can include the Unit Tests on our Continuous Integration Server; also check out Odin, a promising looking project on CodePlex that aims to get the tests running from Resharper.

Oh, and one last thing. Did you know that Microsoft provide free hosting for Silverlight applications – 10 whole Gigabytes? And with Office Live you can get a website hosted for you, but using your own domain name – again for free. That’s how I’m hosting the utility.

Get the Code

I’ve put all the code for this little utility on MSDN Code Gallery for your hacking pleasure.

Friday, 1 May 2009

Practical LINQ #3: Compacting ranges of numbers

Here’s a problem that has popped up several times in one guise or another during the six years of my career:

Given a list of numbers, present it to the user in its most compact form.

So if you have 1, 3, 4, 5, 9, 11, 12, 13 it should be presented as 1, 3 – 5, 9, 11 – 13. It’s what your brain does automatically when you fill out the “Selected Pages” box in the Print dialog of Microsoft Word.

I’m sure you can imagine for yourself the kind of for-loop I crafted the last time I solved this problem. This time I wanted to make life more interesting.

The first thing is to make sure the numbers are marshalled in ascending order, with no duplicates in the ranks:

var preparedList = numbers.Distinct().OrderBy(x => x);

For the next step, I imagined into being a little Scanner class, with a nice fluent interface. I wrote down the following code, which was how I wanted the Scanner to work; then, with Resharper pointing out my omissions in glaring red text, I implemented the concepts to make it work:

public IEnumerable<Range> Compact(IEnumerable<int> numbers)
{
    var preparedList = numbers.Distinct().OrderBy(x => x);

    var scanner = new Scanner<int, Range>();

    scanner.ConfigurePattern()
        .StartMatchingWhenAny()
        .ContinueMatchingWhile((nextItem, matchedItems) => nextItem == matchedItems.Last() + 1)
        .Output(details => new Range(
                                    details.MatchedItems.First(),
                                    details.MatchedItems.Last()));

    return scanner.Scan(preparedList);
}

The idea is for the Scanner to walk a sequence of objects (ints in this case), producing from it a sequence of tokens (here, Range objects – simple structs with First and Last properties). The Scanner produces its output by matching patterns of contiguous input elements. The patterns are configured using a fluent interface:

  • the condition that must be met in order to start matching this pattern (any integer is accepted at the start of this simple pattern)
  • which items are considered part of the pattern: here an integer is accepted if it is only one bigger than the previous matched integer
  • how to generate a token from the matched part of the sequence

The internal workings of the scanner aren’t terribly interesting. See the download link below if you’re desperate for a look.

Now don’t laugh: String.Join is my discovery of the week. It concatenates an array of strings, putting a separator of your choice between them; leaving you free to fry bigger fish than how to put a comma after every item except the last.

So my final answer looks like this:

var numbers = new[] {1, 3, 4, 5, 9, 11, 12, 13};

var compacter = new RangeCompacter();
var ranges = compacter.Compact(numbers);

// create a pretty string, each range or number separated by a comma
var display = string.Join(
    ", ", 
    ranges
    .Select(range => range.ToString())
    .ToArray());

Assert.AreEqual("1, 3 - 5, 9, 11 - 13", display);

I’ve put all the code, including my Scanner class, on Code Gallery; there’s even a sprinkling of unit tests! If you find any other uses for it, do let me know.