gouy_e
gouy_e

Reputation: 188

OrderBy list by a nested list

I have a objectA list, each one contain an another objectB list. I'm trying to OrderBy each objectB list with an (int) Id :

var sorted = objectA.OrderBy(a => a.ObjectB.OrderBy(b => b.Id)).ToList();

Of course, this doesn't work. Someone have an advice ?

Upvotes: 2

Views: 7037

Answers (2)

vgru
vgru

Reputation: 51204

If you want to sort each ObjectB list in place (i.e. modify it), then simply use the List<T>.Sort method. You will need to specify a custom Comparison<T> delegate:

foreach (var a in objectA)
{
   a.ObjectB.Sort((x, y) => x.Id - y.Id)
}

If objectA is a List<ObjectA>, then you can use the ForEach method and pass a delegate:

objectA.ForEach(a => a.ObjectB.Sort((x, y) => x.Id - y.Id));

If you don't want to modify your original ObjectA instances, then you will have to project each ObjectA instance into a new instance (by cloning it) and then assign sorted ObjectB lists. It would look something like (presuming that all properties have public setters):

var newList = objectA
    .Select(x => new ObjectA()
    {
        Id = x.Id,
        SomethingElse = x.SomethingElse,
        ObjectB = x.ObjectB.OrderBy(b => b.Id).ToList()
    })
    .ToList();

Upvotes: 4

user2160375
user2160375

Reputation:

You can prepare extension method and use it at a generic way. For first, helpers for Expression:

public static void SetProperty<T, B>(
                        this Expression<Func<T, B>> propertySelector, 
                        T target, 
                        B value)
{
  SetObjectProperty(target, propertySelector, value);
}

public static void SetObjectProperty<T, B>(
                             T target, 
                             Expression<Func<T, B>> propertySelector, 
                             object value)
{
  if (target == null)
  {
    throw new ArgumentNullException("target");
  }

  if (propertySelector == null)
  {
    throw new ArgumentNullException("propertySelector");
  }

  var memberExpression = propertySelector.Body as MemberExpression;
  if (memberExpression == null)
  {
    throw new NotSupportedException("Cannot recognize property.");
  }

  var propertyInfo = memberExpression.Member as PropertyInfo;
  if (propertyInfo == null)
  {
    throw new NotSupportedException(
                       "You can select property only."
                       + " Currently, selected member is: " 
                       + memberExpression.Member);
  }

  propertyInfo.SetValue(target, value);
}

Then write this extension:

public static IEnumerable<TSource> OrderInnerCollection<TSource, TInner, TKey>(
    this IEnumerable<TSource> source,
    Expression<Func<TSource, IEnumerable<TInner>>> innerSelector,
    Func<TInner, TKey> keySelector)
{
    var innerSelectorDelegate = innerSelector.Compile();
    foreach (var item in source)
    {
        var collection = innerSelectorDelegate(item);
        collection = collection.OrderBy(keySelector);
        innerSelector.SetProperty(item, collection);

        yield return item;
    }
}

And usage:

var result = objectA.OrderInnerCollection(
                           aObj => aObj.ObjectB, 
                           objB => objB.Id).ToList();

Upvotes: 0

Related Questions