tags:

views:

51

answers:

2

I have a large unsorted list of items. Some items are important and need to listed first, followed by unimportant items. The items should be sorted by name in the two groups. I have a solution, but I believe it can be optimized. First, it gets a list of important items. Then a list of everything else, then concatenates the results. Any suggestions on how to optimize this?

Here is a simplified version of the problem for LINQPad:

var doc = XDocument.Parse(@"
<items>
    <item id='a'>not important4</item>
    <item id='b'>important2</item>
    <item id='c'>not important2</item>
    <item id='d'>not important3</item>
    <item id='e'>important1</item>
    <item id='f'>not important1</item>
</items>");
// identify which items are important
string[] importantItemIDs = new string[] { "b", "e" };
var items = doc.Root.Elements("item");

// get a list of important items (inner join)
var importantList = from itemID in importantItemIDs
            from item in items
            orderby (string) item.Value
            where itemID == (string) item.Attribute("id")
            select item;

// get items that are not important items           
var notImportantList = items.Except(importantList).OrderBy(i => (string) i.Value);

// concatenate both sets of results into one list
var fullList = importantList.Concat(notImportantList);
fullList.Select(v => v.Value).Dump();

Here's the correct output:

important1
important2
not important1
not important2
not important3
not important4
+4  A: 

One approach that immediately comes to mind is to utilize OrderBy as well as ThenBy to avoid querying the original data source multiple times. Something like:

var list = items
       .OrderBy(i => importantItemIDs.Contains(i.Attribute("id") ? 0 : 1)
       .ThenBy(i => i.Value);
       .Select(i => i.Value);

I'm not sure if the ternary operator is necessary there - I forget how OrderBy deals with boolean results. Shouldn't be a major performance concern anyway and is potentially a bit clearer.

Ryan Brunner
Both answers posted so far dramatically improve the original performance and perform similarly well. This answer gets the nod for being a bit more elegant. Thanks.
soconnor
But neither gets an up-vote?
James Curran
+1  A: 
var prioritized = 
        from item in items 
        select new {
        Importance = importantItemIDs.Contains((string) item.Attribute)? 1 :2,
        Item = item
        };

var fullList =   from pitem in prioritized 
                 orderby pitem.Importance, pitem.Item.Value
                 select pitem.Item.Value;
James Curran