Feed on
Posts
Comments

Here’s a problem that comes up occasionally.   I have a list of words  that are in a specific order.

apple, banana, cherry, durian,  eggplant, fig, grape, honey, ice, jam

I need to preserve the order, but start the list with a item found later in the list.

eggplant, fig, grape, honey, ice, jam, apple, banana, cherry, durian.

As you can see the original list started with apple, and the new list starts with eggplant.  The words that were at the beginning are now appended after the former last word (jam).

Stated another way, I need to shift the data in the list.  I’ve used different techniques in the past to solve the problem but this time I decided to try LINQ to Objects.

Using LINQ to solve the problem

I love using LINQ to Objects to query and manipulate my data.   Thinking about my application data from a query perspective is so liberating.  LINQ is not always the best choice, at times you need to consider performance, but I thought I would see if I could write a LINQ query to solve this problem.

Using LINQPad

I suspect you know about LINQPad.  This free tool is the best utility I know for exploring LINQ syntax.  I’ll be using it for the screenshots in this article.

http://www.linqpad.net/

Better Version

Update [September 24th, 2011]

@ianmercer on twitter suggested an elegant solution to the problem. Shorter and simpler than my version. Here it his code.

string[] words = { "apple", "banana", "cherry", "durian",
                                                        "eggplant", "fig", "grape", "honey", "ice", "jam" };

    var offset  = 3;

    words.Skip(offset).Concat(words.Take(offset));

I’ve left the original content below, for context, but I suggest you use this improved version.

 

Indexing into a List

[Original solution][

There is a simple technique in LINQ for indexing into another list.  I'll use it in my final query.  Here are the basics of how it works.

First I create the source list.

string[] words = { "apple", "banana", "cherry", "durian",
                            "eggplant", "fig", "grape", "honey", "ice", "jam" };

Then I create an integer array that specifies the preferred order of the data.

// this integer array contains my preferred order for the word list
int[] preferredOrder = { 2, 4, 6, 8, 0, 9,7,5,3 };

Now I'll use this simple LINQ query to get the data and project it into an anonymous type.

var q =  from index in preferredOrder
            select new{Position=index, Word=words[index]};

And here are the results.

image

As you can see the word are presented in the order I chose.

Shifting the list

I use the similar technique to shift the list.  I start by creating the same source list as before.  Next I generate a list of integers, one for each item in the source list.  Note that the integer array starts at zero.

int[] preferredOrder = Enumerable.Range(0,words.Length).ToArray();

 image

Now I use an orderby clause and the mod operator to shift the data.

var offset = 0;

var q1 =  from n in preferredOrder
orderby ((n + offset)  % preferredOrder.Length)
select new{Position=n, Word=words[n]};

image

Problem solved!

UPDATE

September 22.

Here's another approach to the problem from @AbdouMoumen (Twitter).

 

string[] words = { "apple", "banana", "cherry", "durian",
                     "eggplant", "fig", "grape", "honey", "ice", "jam" };
var result = words
                      .Select (
                               (item,index) =>new {
                                Item=item,
                                Index=index
                                                   })
                  .OrderBy( indexedItem => (indexedItem.Index + 6 ) % words.Length )
                  .Select ( indexedItem => indexedItem.Item );
result.Dump();

Comments are closed.