Dot Net Thoughts

December 22, 2007

Merry Christmas!

Filed under: csharp — dotnetthoughts @ 10:39 am
Tags: , ,

Merry Christmas everyone! I hope this holiday finds you happy and healthy with your loved ones! We’ve made the journey north to Washington to be with our families, and the kids are very excited for Christmas this year.

In celebration of Christmas, I thought that I would share with you a coded Christmas tree. I learned of this Christmas tree back in college when I was taking a math methods class, and first programmed it on my trusty TI-85 graphing calculator.

 To build this tree, we’re going to play a simple game. It has three rules:

  1. Define three points that represent the verticies of a traingle traingle.
  2. Starting from one of the verticies, move half the distance to a randomly chosen vertex point, and draw a new point.
  3. Starting at the new point, move half the distances to a randomly chosen vertex point and draw a new point.
  4. Repeat step 3 until you get bored.

Let’s implement the steps in order. We’ll simply use a windows form project and paint the results directly on the form itself.

Our first step is the definition of the verticies. We’ll declare three points forming our triangle as member variables on our form.

        Point _initialPoint1 = new Point(200, 0); 
        Point _initialPoint2 = new Point(0, 400); 
        Point _initialPoint3 = new Point(400, 400);

Next, we will need a method to draw our individual points on the form itself. My DrawPoint method accepts a point and a graphics object. Accepting the graphics object as a parameter prevents us from continually having to create and dispose the graphics object.

        private void DrawPoint(Point point, Graphics g) 
        { 
            Pen pen = new Pen(Color.Green); 
            g.DrawRectangle(pen, point.X, point.Y, 1, 1); 
        }

To implement steps two and three, we will need a method which, given a point, will calculate the half the distance to one of the original verticies and return a new point. You’ll notice we had to create a new member variable called _random. I initially was creating a new random method within the function, itself, but I was getting decidely unrandom results. When the Random object is created, it uses a seed value from the system time. My method was getting called faster than the time was changing, so I was seeing repeated “random” numbers. By moving the object creation outside of the method, the object is seeded only once, and the values turn out to be truly random.

        Random _random = new Random();             

        private Point GetNextPoint(Point startPoint) 
        { 
            Point pointToMoveTo = new Point(); 
            int randomValue = _random.Next(3); 
            int newX = 0; 
            int newY = 0;    
          
            if (randomValue == 0) pointToMoveTo = _initialPoint1; 
            else if (randomValue == 1) pointToMoveTo = _initialPoint2; 
            else if (randomValue == 2) pointToMoveTo = _initialPoint3;         
     
            newX = (startPoint.X + pointToMoveTo.X) / 2; 
            newY = (startPoint.Y + pointToMoveTo.Y) / 2;                   

            return new Point (newX, newY); 
        }

Finally, we just need a method to iterate over it several times. I’m running my 50000 times. As always, be sure you dispose any Graphics objects you create.

        private void ChristmasTree_Load(object sender, EventArgs e) 
        { 
            Point currentPoint;               

            this.Show();               

            using (Graphics g = this.CreateGraphics()) 
            { 
                currentPoint = _initialPoint1; 
                for (int i = 0; i < 50000; i++) 
                { 
                    currentPoint = GetNextPoint(currentPoint); 
                    DrawPoint(currentPoint, g); 
                } 
            }  
        }

 Excellent. Let’s build and run our Christmas tree progam and see what comes out.

ChristmasTree

Isn’t that neat? This code generates a well known fractal called a Sierpinski triangle. An entertaining (non-code) alternative to creating the traingle is to write out Pascal’s triangle, and shade in all of the odd numbers. Pretty neat stuff!

Merry Christmas, all! Code Safe!

MW

——————————Complete Code listing——————————————————-

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace ChristmasTree 
{ 
    public partial class frmChristmasTree : Form 
    { 
        public frmChristmasTree() 
        { 
            InitializeComponent(); 
        }          

        Point _initialPoint1 = new Point(200, 0); 
        Point _initialPoint2 = new Point(0, 400); 
        Point _initialPoint3 = new Point(400, 400); 
        Random _random = new Random();          

        private void ChristmasTree_Load(object sender, EventArgs e) 
        { 
            Point currentPoint;             
            this.Show();             
            using (Graphics g = this.CreateGraphics()) 
            { 
                currentPoint = _initialPoint1; 
                for (int i = 0; i < 50000; i++) 
                { 
                    currentPoint = GetNextPoint(currentPoint); 
                    DrawPoint(currentPoint, g); 
                } 
            }  
        }          

        private void DrawPoint(Point point, Graphics g) 
        { 
            Pen pen = new Pen(Color.Green); 
            g.DrawRectangle(pen, point.X, point.Y, 1, 1); 
        }          

        private Point GetNextPoint(Point startPoint) 
        { 
            Point pointToMoveTo = new Point(); 
            int randomValue = _random.Next(3); 
            int newX = 0; 
            int newY = 0;   
           
            if (randomValue == 0) pointToMoveTo = _initialPoint1; 
            else if (randomValue == 1) pointToMoveTo = _initialPoint2; 
            else if (randomValue == 2) pointToMoveTo = _initialPoint3;              

            newX = (startPoint.X + pointToMoveTo.X) / 2; 
            newY = (startPoint.Y + pointToMoveTo.Y) / 2;              

            return new Point (newX, newY); 
        } 
    } 
}
Advertisements

December 14, 2007

The Linq Jukebox – Part 2 (LINQ Joins)

Filed under: csharp — dotnetthoughts @ 11:47 pm
Tags: , , ,

Last week, I blogged about creating a jukebox using LINQ to query the filesystem. We used a quick-and-dirty query to join properties from three different objects (two directory objects and a file object) into an anonymous type which represented the music folder hierarchy on my machine. It was a pretty neat first attempt at using LINQ, but I want my jukebox to do more.

My goal for this blog is to append additional information to the individual tracks in my library. To do this, I will store comments on individual tracks in an xml file. Each xml element will contain the filename of the track to which the comment applies, as well as the comment itself. Using the filename as a key, we will use LINQ to join the additional Xml information into the data generated from the directory structure.

The xml file is structured as follows. (The file attribute is truncated for space in this posting.)

<TrackData>   
   <Track file="C:\...\Aerosmith\Get A Grip\4 Fever.mp3" comment="Covered by Garth Brooks"/>   
   <Track file="C:\...\The Big Horn Brass\Christmas With The Big Horn Brass\10 Let It Snow.mp3" comment="Merry Christmas!"/> 
</TrackData>

The first step in this process will be to load the Xml. LINQ introduces a whole new series of objects for dealing with Xml data. When using LINQ, XDocuments are used to hold Xml data. These can be loaded directly from a file in much the same you would load the more familiar XmlDocument object.

     XDocument xmlComments = XDocument.Load(Path.Combine(musicFolder, "CustomData.xml"));

We’re now going to loop through each of the Track elements and extract both the file name and the comment from the data. We will start at the DocumentElement and drill down into the XML DOM object using the Elements method. This new method returns an IEnumerable list of type XElement. Using each of these elements, we will create a new anonymous type from the element’s attributes and store a collection of them in the variable comments.

            var comments = 
                from comment in xmlComments.Elements("TrackData").Elements("Track") 
                select new { 
                    File = (string)comment.Attribute("file"), 
                    Comment = (string)comment.Attribute("comment")};

Next, we will take the LINQ query that we created in last week’s blog, and place it in a variable called tracks.

            var tracks = 
                from artistDirectory in topDirectory.GetDirectories() 
                from albumDirectory in artistDirectory.GetDirectories() 
                from file in albumDirectory.GetFiles("*.mp3", SearchOption.TopDirectoryOnly) 
                select new { 
                     Track = Path.GetFileNameWithoutExtension(file.Name), 
                     Album = albumDirectory.Name, 
                     Artist = artistDirectory.Name, 
                     TrackFile = file.FullName 
                };

Now comes the fun part. Using JOIN syntax very similar to SQL, we can take these two different types and merge them based on a primary/foreign key style relationship.

            var mergedResults = 
                from t in tracks 
                from c in comments 
                where c.File == t.TrackFile 
                select new 
                { 
                    Artist = t.Artist, 
                    Album = t.Album, 
                    Track = t.Track, 
                    TrackFile = t.TrackFile, 
                    Comment = c.Comment 
                };

For those of you following along at home, you’ll be quick to point out that this is not quite the result we’re looking for. This LINQ query returns an INNER JOIN style result. I have 50 or so tracks on my laptop, but only two entries in my Xml file. Using this query, I can only access the two tracks that exist in the Xml. What we really want is a LEFT OUTER JOIN. We wish to include all tracks, even if they don’t have a comment associated with them.

It takes a little bit to convince LINQ to do a flattened OUTER JOIN. First, we will do a group join on our Xml and store our results in a temporary variable (tracksAndComments) using the DefaultIfEmpty() method. DefaultIfEmpty will force a null into the right hand side of the join if no data matches the key. Next, we will export this data into a new anonymous type to be stored in mergedResults. We will use a ternary operator to replace any null tracksAndComments objects with an empty string.


            var mergedResults = 
            	from t in tracks 
            	join c in comments on t.TrackFile equals c.File into tracksAndComments 
            	from tc in tracksAndComments.DefaultIfEmpty() 
            	select new 
            	{ 
                   Artist = t.Artist, 
                   Album = t.Album, 
                   Track = t.Track, 
                   TrackFile = t.TrackFile, 
                   Comment = tc == null ? String.Empty : tc.Comment 
            	};

That’s all it takes. The mergedResults variable now contains track information and comments. In other words, we have done the following in eight lines of code:

  • Queried the file system, returned data nested two folders deep, and formatted them into a simplified object.
  • Loaded an XmlDocument, queried it to retrieve all track types, and stored the attribute values in into a simplified object.
  • Merged both of these objects into a single result set.
  • Iterated over the results and passed them on to another method for processing.

Not bad for eight lines of work. I’ve included the complete method below.

Hope you find this helpful. Good luck and code safe!

MW

        private void LoadMusicData() 
        { 
            string musicFolder = @"C:\Users\Mike\Music\iTunes\iTunes Music";           

            DirectoryInfo topDirectory = new DirectoryInfo(musicFolder); 
            XDocument xmlComments = XDocument.Load(Path.Combine(musicFolder, "CustomData.xml"));           

            var comments = 
                from comment in xmlComments.Elements("TrackData").Elements("Track") 
                select new 
                { 
                    File = (string)comment.Attribute("file"), 
                    Comment = (string)comment.Attribute("comment") 
                };           

            var tracks = 
                from artistDirectory in topDirectory.GetDirectories() 
                from albumDirectory in artistDirectory.GetDirectories() 
                from file in albumDirectory.GetFiles("*.mp3", SearchOption.TopDirectoryOnly) 
                select new { 
                     Track = Path.GetFileNameWithoutExtension(file.Name), 
                     Album = albumDirectory.Name, 
                     Artist = artistDirectory.Name, 
                     TrackFile = file.FullName 
                }   

            var mergedResults = 
            	from t in tracks 
            	join c in comments on t.TrackFile equals c.File into tracksAndComments 
            	from tc in tracksAndComments.DefaultIfEmpty() 
            	select new 
            	{ 
                   Artist = t.Artist, 
                   Album = t.Album, 
                   Track = t.Track, 
                   TrackFile = t.TrackFile, 
                   Comment = tc == null ? String.Empty : tc.Comment 
            	};           

            foreach (var mergeResult in mergedResults) 
            { 
                AddListViewItem(mergeResult.Artist, mergeResult.Album, mergeResult.Track, 
                      mergeResult.Comment, mergeResult.TrackFile); 
            }           

        }

December 9, 2007

The Linq Jukebox

Filed under: csharp — dotnetthoughts @ 8:18 am
Tags: , , ,

Earlier this week, the Portland Area Dot Net Users’ Group had an installation party for Visual Studio 2008. During the event, they had a contest to see who could come up with the best LINQ sample. The winner would receive a customized Zune. While I had not yet used LINQ, I decided to throw my hat in the ring with the following query:

     var query = from ZuneWinner in db.PeopleInRoom 
     where ZuneWinner.FirstName == "Michael" and   
     ZuneWinner.LastName=="Weier" 
     select new {ZuneWinner.FirstName, ZuneWinner.LastName};

As you’ve probably already guessed, I didn’t leave with the fancy new piece of hardware. I did receive a chuckle from the judge, though.

After everything had ended, I came up with an idea that may have been a serious contender. A cool entry would have been to try and model an mp3 player’s functionality using LINQ. I decided to create a program that would rip through the music structure on my PC and display the results by artist, album and track.

On my laptop, I have an ITunes folder. (Can you say ITunes on an essentially Microsoft blog?) This folder arranges music into three different levels. The topmost folder contains one folder for each artist I have music for on my PC. Each artist folders contains one folder for each album I have by this artist. The artist folder, in turn, contains a list of tracks that I have available to play on my pc.

LinqFolders

My ultimate goal is to take this hierarchical structure and flatten it into a listbox view similar to the following:

LinqListbox

Traditional (pre-LINQ) programming would have achieved this through a simple nested-loop construct. Starting at the top level folder, loop through the subfolders populating the ListBox’s ListItems as you go.

 
        private void LoadMusicData2() 
        { 
            string musicFolder = @"C:\Users\Mike\Music\iTunes\iTunes Music"; 
            DirectoryInfo topDirectory = new DirectoryInfo(musicFolder); 
            foreach (DirectoryInfo artistDirectory in topDirectory.GetDirectories()) 
            { 
                foreach (DirectoryInfo albumDirectory in artistDirectory.GetDirectories()) 
                { 
                    foreach (FileInfo trackFile in albumDirectory.GetFiles("*.mp3", 
                        SearchOption.TopDirectoryOnly)) 
                    { 
                        AddListViewItem(artistDirectory.Name, albumDirectory.Name, 
                            Path.GetFileNameWithoutExtension(trackFile.ToString()), trackFile.FullName); 
                    } 
                } 
            } 
        } 

This method works well enough, but LINQ gives us a much more elegant solution. In the above code, we have three different objects. The first object maintains artist directories, the second maintains album directories, and the third maintains track information. By using LINQ, we can create an anonymous type which will hold only the pieces of data we are interested in dealing with. The following method is the LINQ equivalent to the above code.

 
        private void LoadMusicData() 
        { 
            string musicFolder = @"C:\Users\Mike\Music\iTunes\iTunes Music"; 
            DirectoryInfo topDirectory = new DirectoryInfo(musicFolder); 
            var query = 
                from artistDirectory in topDirectory.GetDirectories() 
                from albumDirectory in artistDirectory.GetDirectories() 
                from file in albumDirectory.GetFiles("*.mp3", SearchOption.TopDirectoryOnly) 
                select new { Track = Path.GetFileNameWithoutExtension(file.Name), 
                                  Album = albumDirectory.Name, 
                                  Artist = artistDirectory.Name, 
                                  TrackFile = file.FullName};        

            foreach (var trackData in query) 
            { 
                AddListViewItem(trackData.Artist, trackData.Album, trackData.Track, trackData.TrackFile); 
            }         
        } 

The from statements in this query retrieves data from each of the individual folders and merges them into a flattened hierarchy. The select statement then creates a new anonymous type which contains four properties: Track, Album, Artist, and TrackFile. Not only has the data been reduced to only the data we care about, it has been renamed to make more sense for our application. Finally, we loop through the data returned in the query, adding the values into the ListView.

So, really, what is so amazing? Is the second method really that much better than the first?

What I really think will set LINQ apart is the fact that it is platform agnostic when it comes to querying data. The same syntax can be used to query databases, objects, and Xml. Furthermore, one is able to extract and merge exactly what one needs from these different types and combine them into specialized types on the fly. Sorting and filtering data in LINQ is very simple. Want to see only music by the Big Horn Brass with the tracks in descending order? No problem.

 
            var query = 
                from artistDirectory in topDirectory.GetDirectories() 
                from albumDirectory in artistDirectory.GetDirectories() 
                from file in albumDirectory.GetFiles("*.mp3", SearchOption.TopDirectoryOnly) 
                where artistDirectory.Name == "The Big Horn Brass" 
                orderby file.Name descending 
                select new { Track = Path.GetFileNameWithoutExtension(file.Name), 
                                  Album = albumDirectory.Name, 
                                  Artist = artistDirectory.Name, 
                                  TrackFile = file.FullName}; 

The biggest drawback I see at the moment is the syntax for non-trivial queries. I’d originally wanted to merge in some Xml comments to a couple of tracks using the equivalent of a LEFT OUTER JOIN, but never quite got it to work right. The syntax for Xml seems to be entirely new and is not immediately intuitive to someone who has used the old model. (Granted, I’ve probably played with LINQ a total of three hours, now, so I can’t complain too much.) I’ve picked up a LINQ book, and will work to figure that one out. I suspect that the syntax will become easier with time and practice.

Is the demo worth a free Zune? Well, if anybody at Microsoft feel so, let me know <grin>.

That’s pretty much it for today.

Code Safe!

MW

Blog at WordPress.com.