stackMeUp
stackMeUp

Reputation: 171

Extracting values from a nested object and sorting them using LINQ

I am having trouble extracting useful information and order them from a rather complex object.
Here is a simplified version of it:

He are the classes:

class myClass
{
    public int id{ get; set; }
    public List<mySubClass> subClasses { get; set; }
}

class mySubClass
{
    public int offset { get; set; }
    public int value { get; set; }
}

Here is an initialised list:

var mm = new List<myClass> {
    new myClass { id= 2,
        subClasses = new List<mySubClass> {
            new mySubClass { offset = 4, value = 3 },
            new mySubClass { offset = 6, value = 5 },
            new mySubClass { offset = 5, value = 4 } } },
    new myClass { id= 2,
        subClasses = new List<mySubClass> {
            new mySubClass { offset = 20, value = 22 },
            new mySubClass { offset = 1, value = 19 } } },
    new myClass { id= 1,
        subClasses = new List<mySubClass> {
            new mySubClass { offset = 0, value = 1 },
            new mySubClass { offset = 7, value = 6 },
            new mySubClass { offset = 1, value = 2 } } } };
   

Here is what I have tried:

var oo = mm.OrderBy(y => y.id).SelectMany(yy => yy.subClasses.OrderBy(z => z.offset)).ToList().Select(y => y.value).ToArray();

What am I trying to do is to extract all the values into an array and sorting them by "offset" value, but without mixing up the different "id".

Somehow, the ordering always ends up getting messed up. I think I am not grouping id's properly and all the "List< mySubClass >" end up getting treated independently from one another.

Expected output should be: 1,2,6,19,3,4,5,22 But I am getting: 1,2,6,3,4,5,19,22

EDIT

In some case, I am also interested in recovering all the "mySubClass" objects.
Therefore, instead of returning an array of values, how could we return a list of "mySubClass" using the same sorting criteria?

Upvotes: 1

Views: 639

Answers (2)

Tim Schmelter
Tim Schmelter

Reputation: 460278

You have to use SelectMany and then store the properties in a type, for example a ValueTuple. Then it's easy to use the proper OrderBy ... ThenBy:

int[] result = mm
   .SelectMany(x => x.subClasses.Select(y => (Id:x.id, Offset:y.offset, Value:y.value)))
   .OrderBy(x => x.Id)
   .ThenBy(x => x.Offset)
   .Select(x => x.Value)
   .ToArray();

What if instead of returning the ordered values you wanted to return a list of ordered "mySubClass" based on the same sorting criteria. How would you do that?

mySubClass[] result = mm
    .SelectMany(x => x.subClasses.Select(y => (Id:x.id, mySubClass:y)))
    .OrderBy(x => x.Id)
    .ThenBy(x => x.mySubClass.offset)
    .Select(x => x.mySubClass)
    .ToArray();

Upvotes: 5

Harald Coppoolse
Harald Coppoolse

Reputation: 30512

What am I trying to do is to extract all the values into an array and sorting them by "offset" value

I think you don't want the integer array of only the values of property value. I think you want a sequence of:

int Id
int Offset
int Value

Ordered by ascending value of Offset. If you don't want Offset in your end-result, you can always use an extra Select to get rid of it.

You are right, you do this with one of the overloads of Enumerable.SelectMany. I almost always use the overload that has a parameter resultSelector

IEnumerable<MyClass> items = ...
var result = items.SelectMany(item => item.SubClasses,

// parameter resultSelector, use the [Item, SubClass] combination to make one new
(item, subclass) => new
{
    Id = item.Id,
    Offset = item.Offset,
    Value = item.Value,
})
.OrderBy(item => item.Offset);

Upvotes: 1

Related Questions