Сортировка списка с помощью Lambda/Linq для объектов

Хорошо, понял это в конце. Поэтому мне нужно было использовать mergeMap на основе ответа, а при желании asBase64 создать новую наблюдаемую из события чтения файла onloadend.

Но это также не сработало из-за этих строк (см. Код в моем исходном вопросе).

let reader = new FileReader();
                    let eventObservable = fromEvent(reader, 'onloadend').pipe(map(() => {
                        return reader.result;
                    }));
                    reader.readAsDataURL(data);
                    return eventObservable;

Проблема с подходом fromEvent состоит в том, что событие запускается только один раз, и в моем случае оно срабатывало до того, как мой шаблон Angular смог подписаться на наблюдаемое, решение состояло в том, чтобы использовать Subject, не уверенный, если это идеально в этом случае, но мне кажется, что все в порядке.

transform(url: string, asBase64: boolean): Observable<any> {
        return this.http
            .get(url, {responseType: 'blob'})
            .pipe(map(val =>
                <any>(asBase64 ? val : this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val)))
            )).pipe(mergeMap(data => {
                if (!asBase64) {
                    return of(data)
                } else {
                    let reader = new FileReader();
                    let subject = new Subject<any>();
                    reader.onloadend = () => {
                        subject.next(reader.result);
                    };
                    reader.readAsDataURL(data);
                    return subject;
                }
            }));
    }
264
задан Jim Balter 26 December 2016 в 21:27
поделиться

9 ответов

Одна вещь, которую Вы могли сделать, измениться Sort таким образом, это лучше использует лямбды.

public enum SortDirection { Ascending, Descending }
public void Sort<TKey>(ref List<Employee> list,
                       Func<Employee, TKey> sorter, SortDirection direction)
{
  if (direction == SortDirection.Ascending)
    list = list.OrderBy(sorter);
  else
    list = list.OrderByDescending(sorter);
}

Теперь можно указать поле к виду при вызове Sort метод.

Sort(ref employees, e => e.DOB, SortDirection.Descending);
74
ответ дан bluish 23 November 2019 в 02:28
поделиться

Вы могли использовать Отражение для получения значения свойства.

list = list.OrderBy( x => TypeHelper.GetPropertyValue( x, sortBy ) )
           .ToList();

Где TypeHelper имеет статический метод как:

public static class TypeHelper
{
    public static object GetPropertyValue( object obj, string name )
    {
        return obj == null ? null : obj.GetType()
                                       .GetProperty( name )
                                       .GetValue( obj, null );
    }
}

Вы могли бы также хотеть посмотреть на Динамический LINQ из библиотеки VS2008 Samples. Вы могли использовать расширение IEnumerable, чтобы снять Список в качестве IQueryable и затем использовать Динамический канал расширение OrderBy.

 list = list.AsQueryable().OrderBy( sortBy + " " + sortDirection );
55
ответ дан tvanfosson 23 November 2019 в 02:28
поделиться

Создание порядка по выражению может быть считано здесь

Бесстыдно украденный от страницы в ссылке:

// First we define the parameter that we are going to use
// in our OrderBy clause. This is the same as "(person =>"
// in the example above.
var param = Expression.Parameter(typeof(Person), "person");

// Now we'll make our lambda function that returns the
// "DateOfBirth" property by it's name.
var mySortExpression = Expression.Lambda<Func<Person, object>>(Expression.Property(param, "DateOfBirth"), param);

// Now I can sort my people list.
Person[] sortedPeople = people.OrderBy(mySortExpression).ToArray();
15
ответ дан Rashack 23 November 2019 в 02:28
поделиться

Вы могли использовать отражение для доступа к свойству.

public List<Employee> Sort(List<Employee> list, String sortBy, String sortDirection)
{
   PropertyInfo property = list.GetType().GetGenericArguments()[0].
                                GetType().GetProperty(sortBy);

   if (sortDirection == "ASC")
   {
      return list.OrderBy(e => property.GetValue(e, null));
   }
   if (sortDirection == "DESC")
   {
      return list.OrderByDescending(e => property.GetValue(e, null));
   }
   else
   {
      throw new ArgumentOutOfRangeException();
   }
}

Примечания

  1. Почему Вы передаете список ссылкой?
  2. Необходимо использовать перечисление для направления вида.
  3. Вы могли получить намного более чистое решение при передаче лямбда-выражения, указывающего свойство к виду вместо имени свойства как строка.
  4. В моем списке в качестве примера == пустой указатель вызовет NullReferenceException, необходимо поймать этот случай.
8
ответ дан Daniel Brückner 23 November 2019 в 02:28
поделиться

Вид использует интерфейс IComparable, если тип реализует его. И можно избежать IFS путем реализации пользовательского IComparer:

class EmpComp : IComparer<Employee>
{
    string fieldName;
    public EmpComp(string fieldName)
    {
        this.fieldName = fieldName;
    }

    public int Compare(Employee x, Employee y)
    {
        // compare x.fieldName and y.fieldName
    }
}

и затем

list.Sort(new EmpComp(sortBy));
6
ответ дан Samuel 23 November 2019 в 02:28
поделиться

Ответ для 1.:

Необходимо смочь вручную создать дерево выражений, которое может быть передано в OrderBy с помощью имени в качестве строки. Или Вы могли использовать отражение, как предложено в другом ответе, который мог бы быть меньшим количеством работы.

Править: Вот рабочий пример создания дерева выражений вручную. (Сортирующий на X.Value, только зная имя "Значение" свойства). Вы могли (должен) создать общий метод для того, чтобы сделать его.

using System;
using System.Linq;
using System.Linq.Expressions;

class Program
{
    private static readonly Random rand = new Random();
    static void Main(string[] args)
    {
        var randX = from n in Enumerable.Range(0, 100)
                    select new X { Value = rand.Next(1000) };

        ParameterExpression pe = Expression.Parameter(typeof(X), "value");
        var expression = Expression.Property(pe, "Value");
        var exp = Expression.Lambda<Func<X, int>>(expression, pe).Compile();

        foreach (var n in randX.OrderBy(exp))
            Console.WriteLine(n.Value);
    }

    public class X
    {
        public int Value { get; set; }
    }
}

Создание дерева выражений требует, чтобы Вы знали типы particpating, как бы то ни было. Тот мог бы или не могла бы быть проблема в Вашем сценарии использования. Если Вы не знаете то, что вводит Вас, должен сортировать на, это propably будет более легкое отражение использования.

Ответ для 2.:

Да, начиная с Компаратора <T>.Default будет использоваться для сравнения, если Вы явно не определите компаратор.

5
ответ дан driis 23 November 2019 в 02:28
поделиться

Добавление к тому, что сделали @Samuel и @bluish. Это намного короче, поскольку Перечисление было ненужным в этом случае. Плюс как добавленная премия, когда Возрастание является желаемым результатом, можно передать только 2 параметра вместо 3, так как верный ответ по умолчанию на третий параметр.

public void Sort<TKey>(ref List<Person> list, Func<Person, TKey> sorter, bool isAscending = true)
{
    list = isAscending ? list.OrderBy(sorter) : list.OrderByDescending(sorter);
}
0
ответ дан 23 November 2019 в 02:28
поделиться

Если Вы получаете имя столбца вида и направление вида как строка и не хотите использовать переключатель или if\else синтаксис для определения столбца, то этот пример может быть интересным для Вас:

private readonly Dictionary<string, Expression<Func<IuInternetUsers, object>>> _sortColumns = 
        new Dictionary<string, Expression<Func<IuInternetUsers, object>>>()
    {
        { nameof(ContactSearchItem.Id),             c => c.Id },
        { nameof(ContactSearchItem.FirstName),      c => c.FirstName },
        { nameof(ContactSearchItem.LastName),       c => c.LastName },
        { nameof(ContactSearchItem.Organization),   c => c.Company.Company },
        { nameof(ContactSearchItem.CustomerCode),   c => c.Company.Code },
        { nameof(ContactSearchItem.Country),        c => c.CountryNavigation.Code },
        { nameof(ContactSearchItem.City),           c => c.City },
        { nameof(ContactSearchItem.ModifiedDate),   c => c.ModifiedDate },
    };

    private IQueryable<IuInternetUsers> SetUpSort(IQueryable<IuInternetUsers> contacts, string sort, string sortDir)
    {
        if (string.IsNullOrEmpty(sort))
        {
            sort = nameof(ContactSearchItem.Id);
        }

        _sortColumns.TryGetValue(sort, out var sortColumn);
        if (sortColumn == null)
        {
            sortColumn = c => c.Id;
        }

        if (string.IsNullOrEmpty(sortDir) || sortDir == SortDirections.AscendingSort)
        {
            contacts = contacts.OrderBy(sortColumn);
        }
        else
        {
            contacts = contacts.OrderByDescending(sortColumn);
        }

        return contacts;
    }

Решение на основе использования Словаря, который соединяется необходимый для столбца вида через Выражение> и его строка ключа.

0
ответ дан 23 November 2019 в 02:28
поделиться
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;

public static class EnumerableHelper
{

    static MethodInfo orderBy = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "OrderBy" && x.GetParameters().Length == 2).First();

    public static IEnumerable<TSource> OrderBy<TSource>(this IEnumerable<TSource> source, string propertyName)
    {
        var pi = typeof(TSource).GetProperty(propertyName, BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance);
        var selectorParam = Expression.Parameter(typeof(TSource), "keySelector");
        var sourceParam = Expression.Parameter(typeof(IEnumerable<TSource>), "source");
        return 
            Expression.Lambda<Func<IEnumerable<TSource>, IOrderedEnumerable<TSource>>>
            (
                Expression.Call
                (
                    orderBy.MakeGenericMethod(typeof(TSource), pi.PropertyType), 
                    sourceParam, 
                    Expression.Lambda
                    (
                        typeof(Func<,>).MakeGenericType(typeof(TSource), pi.PropertyType), 
                        Expression.Property(selectorParam, pi), 
                        selectorParam
                    )
                ), 
                sourceParam
            )
            .Compile()(source);
    }

    public static IEnumerable<TSource> OrderBy<TSource>(this IEnumerable<TSource> source, string propertyName, bool ascending)
    {
        return ascending ? source.OrderBy(propertyName) : source.OrderBy(propertyName).Reverse();
    }

}

Еще один, на этот раз для любого IQueryable:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

public static class IQueryableHelper
{

    static MethodInfo orderBy = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "OrderBy" && x.GetParameters().Length == 2).First();
    static MethodInfo orderByDescending = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "OrderByDescending" && x.GetParameters().Length == 2).First();

    public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, params string[] sortDescriptors)
    {
        return sortDescriptors.Length > 0 ? source.OrderBy(sortDescriptors, 0) : source;
    }

    static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string[] sortDescriptors, int index)
    {
        if (index < sortDescriptors.Length - 1) source = source.OrderBy(sortDescriptors, index + 1);
        string[] splitted = sortDescriptors[index].Split(' ');
        var pi = typeof(TSource).GetProperty(splitted[0], BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.IgnoreCase);
        var selectorParam = Expression.Parameter(typeof(TSource), "keySelector");
        return source.Provider.CreateQuery<TSource>(Expression.Call((splitted.Length > 1 && string.Compare(splitted[1], "desc", StringComparison.Ordinal) == 0 ? orderByDescending : orderBy).MakeGenericMethod(typeof(TSource), pi.PropertyType), source.Expression, Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(TSource), pi.PropertyType), Expression.Property(selectorParam, pi), selectorParam)));
    }

}

Вы можете передавать несколько критериев сортировки, как здесь:

var q = dc.Felhasznalos.OrderBy(new string[] { "Email", "FelhasznaloID desc" });
4
ответ дан 23 November 2019 в 02:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: