A couple of weeks ago, I pointed out a LINQ snippet that was running against the Google Charts API.
This week, I’m back with my implementation of the Pie Chart.
I’ve started with the Pie chart because its relatively simple and will lay the foundation for dealing with the complexities of the other charts.
Building Blocks
Since this is MVC, I created a new ChartsService class in the Services namespace of the project.
If you look at the snippets in the original ACM article, you’ll notice an extension method called “SeparatedBy”. So our first task is to create this extension method.
There are, of course, a number of ways to concatenate a list of string with a separator. This being an exercise in LINQ, we are going to use the LINQ Aggregate method.
public static string SeparatedBy(this List<String> list, string seperator) { return list.Aggregate((current, next) => current + seperator + next); }
I’m sure that you’ll agree with me when i say that it’s a nice and clean approach to what could be a messy block of code.
However, that extension method will only concatenate the list of strings passed to it. Why is this a problem? Because we are going to want to concatenate lists of int objects as well. So that extension method will just not do. We could do some fancy work with generics, but the simplest thing to do is to supply to an overloaded method that accepts lists of type int.
public static string SeparatedBy(this List<int> list, string seperator) List<string> thelist = list.ConvertAll<string>(x => x.ToString()); return thelist.Aggregate((current, next) => current + seperator + next); }
There is one additional difference between these and our original method – the call to ConvertAll. Rather than have a foreach loop that does the conversation, we simply supply an inline lambda function that gets executed against each item in the list and returns a list of the desired type. Again, very clean.
So, armed with these extension methods, we can now declare our classes.
Data
Google charts offers a wide range of functionality and many different kinds of charts. Each chart has a host of differing options and settings available to it. So when creating classes to represting thse charts kinds we have to bear in mind that there will be unique functionality not common to other charts that will come up.
Charts logically are made up of a number of smaller complements: bar charts have columns, pie charts have slices and so on and so forth. So we’ll represent these first.
public class Slice { public int Value { get; private set; } public string Legend { get; private set; } public Slice(int value, string legend) this.Value = value; this.Legend = legend; } }
Lets first look at the Pie class itself now.
Pie Class
public List<Slice> Slices { get; set; } public string Title { get; private set; public Double Height { get; private set; } public Double Width { get; private set; } public Pie(string title, double height, double width) { this.Slices = new List<Slice>(); this.Title = title; this.Height = height; this.Width= width; }
We start by declaring a number of properties and a constructor. In the constructor we initialize our list of Slices. This is where we see a departure from the snippets of he ACM article. We do not pass the slices into the constructor. Of course, this is an issue of style over substance. There is no reason why we could not have generated the slices before the creating the chart and then passed the slices.
public string Compile() { var tt = Title.Split(' ').ToList().SeparatedBy(' '); var p = new List<string>(){ this.Slices.Select(slice =>slice.Legend).ToList().SeparatedBy("|"), this.Slices.Select(slice =>slice.Value).ToList().SeparatedBy(",")}; return string.Format(@"http://chart.googleapis.com/chart?cht=p&chtt={0}&chs={3}x{4}&chl={1}&chd=t:{2}", tt, p.ElementAt(0), p.ElementAt(1), this.Width, this.Height) }
This is where all the important stuff happens.
Line 3 properly formats the title by putting a + sign to signify spaces between words. Note that we are using the extension method we wrote earlier.
Line 4 creates a new list of strings, each string being the comma or | delimited list of values and legends. Using a List gives us greater flexibility later on when our implementation will handle multiple data series and legends
Line 8 uses String.Format to merge all this data into a url that we can use to call the Google Charts API with. For an explanation of what each of these parameters mean, see the Google Charts API Parameter List
ViewModel
Now, this being MVC, the way to display stuff is by using a ViewModel. So lets create one:
public class MonthlyReportsViewModel { public MonthlyReports Details { get; set; } public Pie Chart { get; set; } }
The one property we are interested in here is the Chart Property.
Now that we have our ViewModel, we have to populate it. So, in our ActionResult method:
<pre>Pie chart = new Pie("This is my chart",200); chart.Slices.Add(new Slice(25, “Slice 1”)); chart.Slices.Add(new Slice(25, “Slice 2”)); chart.Slices.Add(new Slice(25, “Slice 3”)); chart.Slices.Add(new Slice(25, “Slice 4”)); model.Chart = chart; return View(model);
View
In our View itself, we’re going to have to render out the chart. Since the call to Google Charts API will return an image, we can simply do the following:
<img src = "@Model.Chart.Compile()" alt ="">
What one could do is to put the actual rendering code in a Helper Method and call the Helper Method from your view, like so:
@GoogleCharts.PieChart(@Model.Chart)
That, of course, further abstracts the code. It does have the advantage of being much cleaner and easier to do.
Conclusion
As you can see, using LINQ to abstract away the complexity of what your code is actually doing is not just the province of database code. One thing what I’ve enjoyed about working with LINQ is how code always comes out looking fresh, clean and crisp. Having worked extensively with LINQ and Lambda expressions, using foreach loops to process Lists looks so much messier.
Next time, we’ll take a look at the somewhat more complicated Bar Chart. I’ll not cover every single possible piece of functionality, but I’ll cover the basics. All I want to show is how the foundation laid down today can easily translate over. My current implementation of bar charts is sufficient only for the limited functionality the app needs and nothing more.
At some point in the future, I’d also like to implement Line charts.
Postscript
I must say that apart from working with LINQ, its been a very satisfying experience for me to implement a C# version of a web API.
There is GoogleChartsSharp on Codeplex that Implements a whole lot more of the functionality of Google Charts. I did indeed use it for a while before implementing it on my own.
So its been a satisfying experience for me to implement an API that allows me to work the way I want to work. Not only did I write something simpler and easier to work with, but I dropped a dependency in the process and that made me happier than I think its safe to admit.
Writing against something requires you, the writer, to pay extra attention to the small details. It requires you to think of the relationship between your code,the web API calls and the documentation that supports it. When one uses an already baked implementation such as GoogleChartSharp, its like working with a giant black box you have no idea what goes on inside. And you really don’t want to know the finer details. But writing the API, you create a white box. And you HAVE to understand those finer details.
So while the LINQ is nothing special in and of itself,nothing earth shattering or ground-breaking, it is the experience and the satisfaction gained from it that makes this a worthwhile post to write.
You must be logged in to post a comment.