<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://interactiveasp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Phil Gilmore : Linq</title><link>http://interactiveasp.net/blogs/spgilmore/archive/tags/Linq/default.aspx</link><description>Tags: Linq</description><dc:language>en</dc:language><generator>CommunityServer 2008 (Build: 30417.1769)</generator><item><title>Building data sequences in C#</title><link>http://interactiveasp.net/blogs/spgilmore/archive/2009/05/19/building-data-sequences-in-c.aspx</link><pubDate>Tue, 19 May 2009 20:03:00 GMT</pubDate><guid isPermaLink="false">b80005ef-4071-4968-b08e-765d7d71b33e:768</guid><dc:creator>Phil Gilmore</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://interactiveasp.net/blogs/spgilmore/rsscomments.aspx?PostID=768</wfw:commentRss><comments>http://interactiveasp.net/blogs/spgilmore/archive/2009/05/19/building-data-sequences-in-c.aspx#comments</comments><description>&lt;p&gt;&lt;a href="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceLogo.png"&gt;&lt;img style="border-right-width: 0px; margin: 10px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" alt="SequenceLogo" src="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceLogo_5F00_thumb.png" align="left" border="0" width="258" height="112" /&gt;&lt;/a&gt; 05/19/2009, Phil Gilmore&lt;/p&gt;
&lt;p&gt;Beyond the overlap they share with enumerable collections, sequences are mostly unsupported in C#.&amp;nbsp; A notable exception are some of the static methods of the Enumerable class.&amp;nbsp; Check them out.&amp;nbsp; This support is all I need for most sequence work I do.&amp;nbsp; But the support therein is insufficient for anything complex.&amp;nbsp; To augment this support, I have written some extension methods to various CLR classes to add pseudo-support for sequences and sets.&amp;nbsp; One that seems to be especially useful is a sequence generator method.&amp;nbsp; It immediately became useful in many instances, so I thought I'd share it.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;What can I do with a sequence generator?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Generate a time-of-day list to allow users to select a time from a drop-down list, perhaps in 15-minute intervals.  &lt;/li&gt;
&lt;li&gt;Generate a list of months to allow users to select a month from a drop-down list.  &lt;/li&gt;
&lt;li&gt;Generate a list of years to allow a user to select their birth year from a drop-down list.  &lt;/li&gt;
&lt;li&gt;Generate a list of any type of sequential data for user selection.  &lt;/li&gt;
&lt;li&gt;Build an "upline" of parent-child relationships in table of hierarchical data traversed through a self-join.  &lt;/li&gt;
&lt;li&gt;Any operation that would normally be constructed using a For loop or While loop to populate a list or collection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Why not collections and loops?&lt;/h2&gt;
&lt;p&gt;Looking at this list, you might be pondering the usefulness of this since many of these things can be achieved using a collections built in a for-loop or through various Linq maneuvers.&amp;nbsp; This is true.&amp;nbsp; Consider the example above in building an upline from a hierarchical table.&amp;nbsp; But the value of sequence generation is easier to see when considering that sequence data can be built by analyzing the last item in the sequence.&amp;nbsp; For example, you could use a For loop to iterate through your file system and build a List of mp3 filenames in your music collection.&amp;nbsp; This is more the function of a collection than a sequence.&amp;nbsp; This data already exists as separate elements that are found and stored in a collection.&amp;nbsp; If one mp3 file is deleted, the next does not change.&amp;nbsp; In a sequence where elements are related by generation, the removal or change of an element may affect the generation of the rest of the sequence.&amp;nbsp; For example, consider a sequence of incremental integers starting with 1000 and incrementing by 1 with a sequence size of 5.&amp;nbsp; The elements of this sequence would be 1000, 1001, 1002, 1003 1004.&amp;nbsp; If the first element were to change to 1002, the sequence would continue its pattern and become 1002, 1003, 1004, 1005, 1006.&amp;nbsp; The generation of elements in a sequence also does not necessarily require existing data to be collected.&amp;nbsp; Whereas a collection of mp3 filenames requires a filesystem that can be queried to retrieve these filenames, a sequence of incremental integer is generated from only a single seed element (1000 in my first example).&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Building a sequence generator:&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;T&amp;gt; CreateSequence&amp;lt;T&amp;gt;(&lt;br /&gt;    &lt;span style="color: blue"&gt;this &lt;/span&gt;T startElement, &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T, T&amp;gt; getNextElement, &lt;span style="color: #2b91af"&gt;Predicate&lt;/span&gt;&amp;lt;T&amp;gt; isLastElement)&lt;br /&gt;{&lt;br /&gt;    T currentElement = startElement;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;while &lt;/span&gt;(!isLastElement(currentElement))&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;yield return &lt;/span&gt;currentElement;&lt;br /&gt;        currentElement = getNextElement(currentElement);&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: blue"&gt;yield return &lt;/span&gt;currentElement;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The first parameter (startElement) is the object on which the method is called.&amp;nbsp; This is probably a primitive object, but can be anything.&amp;nbsp; There are no constraints on the T type parameter so reference and value types are both game.&amp;nbsp; Even literals work.&amp;nbsp; You can call this method on the literal number 1.&amp;nbsp; Of course, this is part of the syntax of C# extension methods.&amp;nbsp; When consuming this method, you do not pass the first parameter.&amp;nbsp; But it's important to understand that this is the first element in the sequence (the seed).&amp;nbsp; &lt;/p&gt;
&lt;p&gt;The getNextElement parameter (the first that you provide) is a function.&amp;nbsp; This function can be a delegate to a method, an anonymous method, or a lambda expression.&amp;nbsp; In my examples, I use a lambda expression.&amp;nbsp; The function is given the current element and is expected to return the next element in the sequence.&amp;nbsp; For simple sequences, it will probably just add or subtract some number to the current element to produce the next one.&lt;/p&gt;
&lt;p&gt;The isLastElement parameter is another function.&amp;nbsp; As each element is added to the sequence, this function is executed to determine if the end of the sequence has been reached.&amp;nbsp; If the function returns false, then the sequence continues and getNextElement will be called again to get yet another element.&amp;nbsp; If it instead returns true, the current element will be the last element added to the sequence and the CreateSequence method will return thereafter.&lt;/p&gt;
&lt;p&gt;Beware of infinite loops when using this.&amp;nbsp; If the IsLastElement function never returns true, you will encounter an infinite loop.&amp;nbsp; For this reason, it may be a good habit to use &amp;gt;= and &amp;lt;= operators instead of == operators, for example.&amp;nbsp; Even though your sequence may be simple as in our examples, better safe than sorry.&amp;nbsp; For more complicated sequences, be even more careful.&lt;/p&gt;
&lt;p&gt;The method can be refined if desired.&amp;nbsp; It may be desirable to change the isLastElement predicate from an UNTIL condition to a WHILE condition (and hence renaming it to isValidElement instead), for example.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Using the sequence generator:&lt;/h2&gt;
&lt;p&gt;Most of these examples can be done using the static methods of the Enumerable class with a little more simplicity.&amp;nbsp; But I'll still keep the examples simple.&amp;nbsp; You can build up to the hard stuff yourself.&amp;nbsp; Here is a simple example using the sequence generator method in an ASP.NET MVC web form where a TIME must be selected.&amp;nbsp; First, the code to generate the view data:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Create a sequence of times, 15 minutes apart.&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #2b91af"&gt;DateTime &lt;/span&gt;referenceDate = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now.Date;&lt;br /&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;&amp;gt; availableTimes = referenceDate.CreateSequence&lt;br /&gt;(&lt;br /&gt;    x =&amp;gt; x.AddMinutes(15), &lt;br /&gt;    x =&amp;gt; x.CompareTo(referenceDate.AddDays(1).AddMinutes(-15)) &amp;gt;= 0&lt;br /&gt;).ToList();&lt;br /&gt;&lt;br /&gt;&lt;span style="color: green"&gt;// Convert times to a list of bindable objects for a dropdown list in the MVC view.&lt;br /&gt;&lt;/span&gt;ViewData[&lt;span style="color: #a31515"&gt;"DueTimesAvailable"&lt;/span&gt;] = availableTimes.Select(&lt;br /&gt;    x =&amp;gt; &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SelectListItem&lt;/span&gt;()&lt;br /&gt;    {&lt;br /&gt;        Text = x.ToString(&lt;span style="color: #a31515"&gt;"hh:mm tt"&lt;/span&gt;),&lt;br /&gt;        Value = x.ToString(&lt;span style="color: #a31515"&gt;"hh:mm tt"&lt;/span&gt;),&lt;br /&gt;        Selected = x.Equals(referenceDate)&lt;br /&gt;    }).ToList();&lt;br /&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The first block of code creates a list of datetimes starting at midnight on the current date, every 15 minutes until 11:45pm on the same day.&amp;nbsp; The second block converts these DateTimes to a List&amp;lt;SelectListItem&amp;gt; using Linq.&amp;nbsp; Here's a simple example, creating a list of month names.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt; months = 1.CreateSequence(m =&amp;gt; m + 1, m =&amp;gt; m &amp;gt;= 12)&lt;br /&gt;    .Select(i =&amp;gt; System.Threading.&lt;span style="color: #2b91af"&gt;Thread&lt;br /&gt;        &lt;/span&gt;.CurrentThread.CurrentCulture&lt;br /&gt;        .DateTimeFormat.GetMonthName(i)&lt;br /&gt;    ).ToList();&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;Screen shots:&lt;/h2&gt;
&lt;p&gt;In the example above, we get month names in a sequence ("January", "Februrary", "March"...).&amp;nbsp; Below are some of these examples in action in an ASP.NET MVC page.&amp;nbsp; Notice that Months and Times are in ascending order and Years are in descending order.&amp;nbsp; They were not sorted in this way.&amp;nbsp; They were generated in those orders by either incrementing or decrementing the sequence seed.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceTime_5F00_2.png"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" alt="SequenceTime" src="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceTime_5F00_thumb.png" border="0" width="165" height="305" /&gt;&lt;/a&gt; &lt;a href="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceMonth.png"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" alt="SequenceMonth" src="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceMonth_5F00_thumb.png" border="0" width="166" height="196" /&gt;&lt;/a&gt; &lt;a href="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceYear.png"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" alt="SequenceYear" src="http://interactiveasp.net/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/spgilmore/SequenceYear_5F00_thumb.png" border="0" width="149" height="115" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;So where does this leave sets?&amp;nbsp; &lt;/h2&gt;
&lt;p&gt;Additional extension methods and classes can be used to treat these sequences and native Enums as sets.&amp;nbsp; Watch for more details in another blog post.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://interactiveasp.net/aggbug.aspx?PostID=768" width="1" height="1"&gt;</description><category domain="http://interactiveasp.net/blogs/spgilmore/archive/tags/Visual+Studio/default.aspx">Visual Studio</category><category domain="http://interactiveasp.net/blogs/spgilmore/archive/tags/.NET+3.5/default.aspx">.NET 3.5</category><category domain="http://interactiveasp.net/blogs/spgilmore/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://interactiveasp.net/blogs/spgilmore/archive/tags/Linq/default.aspx">Linq</category></item></channel></rss>