Add Collection to End of IOrderedEnumerable

3.8k views Asked by At

I currently have a bunch of config files I need to load into an IOrderedEnumerable My current approach looks like so:

foreach (var item in configFiles)
{
    XDocument myxml = XDocument.Load(item);
    var items = myxml.Root.Elements("Item");
    Items = items.OrderBy(x => x.Attribute("ID").Value);
    ItemsLength += Items.Count();              
}

The problem is, instead of making Items equal to items.OrderBy(x => x.Attribute("ID").Value) I'd like to join that to the end of the currently existing IOrderedEnumerable so I'm not overwriting it each time I load a new XDocument and get all of the elements from it. How would I do this?

EDIT: I know that if I change this ItemsLength += Items.Count(); will no longer function properly. That's something I'll change on my own.

2

There are 2 answers

1
Jon Skeet On BEST ANSWER

You can do the whole thing declaratively:

Items = configFiles.Select((item, index) => new { Doc = XDocument.Parse(item),
                                                  Index = index })
                   .SelectMany(pair => pair.Doc.Root.Elements("Item")
                                           .Select(x => new { Item = x, 
                                                              Index = pair.Index }))
                   .OrderBy(pair => pair.Index)
                   .ThenBy(pair => (string) pair.Attribute("ID"))
                   .Select(pair => pair.Item);

This basically finds all the elements, but remembers which configuration each is in.

Alternatively, just create a List<XElement> and add each item's contents:

var items = new List<XElement>();
foreach (var item in configFiles)
{
    items.AddRange(XDocument.Parse(item)
                            .Root.Elements("Item")
                            .OrderBy(x => (string) x.Attribute("ID")));
}
Items = items;

In some ways that's less elegant, but it's probably easier to understand :)

1
Sergey Kalinichenko On

If you can change Items to be of type IEnumerable rather than IOrderedEnumerable, you could use Concat:

Items = Items.Concat(items.OrderBy(x => x.Attribute("ID").Value));

This would keep items sorted by ID within their own document.

Note that using Concat in a loop may impair the performance. You would be better off declaring Items as IList, and calling ToList at the end of each iteration:

Items.AddRange(items.OrderBy(x => x.Attribute("ID").Value));