【问题标题】:LINQ How to use multiple ThenBy dynamically?LINQ如何动态使用多个ThenBy?
【发布时间】:2015-03-14 22:35:06
【问题描述】:

我不认为这会如此罕见,但看起来确实如此。情况是这样的。

我有一个 ApplySort 方法,它接受从 mvc 页面发布的 3 个参数。

    private List<dynamic> ApplySort(List<dynamic> listToBeSorted, string sortBy, string sortOrder)
    {
        if (String.IsNullOrEmpty(sortBy))
            sortBy = "createddate";
        if (String.IsNullOrEmpty(sortOrder) || sortOrder.Trim() != "0")
            sortOrder = "1"; // 1 = descending, 0 = ascending
        if (sortOrder == "1")
        {
            switch (sortBy)
            {
                case "name":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.name).ToList();
                    break;
                case "assigned":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
                    break;
                case "duedate":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.end).ToList();
                    break;
                case "status":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
                    break;
                default:
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
                    break;
            }
        }
        else
        {
            // same code as in if-block, with just OrderBy calls instead of OrderByDescending
        }
        return listToBeSorted;
    }

两个问题:

1) 方法似乎过长(if 和 else 块中的代码非常相似)。

2) 我希望能够使用多个列进行排序。 sortBy 参数可以具有诸如“名称、标题、创建日期、状态”之类的值。所以应用的排序应该是,首先按名称,然后按标题,然后按创建日期......等等。 我可以通过顺序检查参数来使用 ThenBy但是如何根据参数值动态应用 ThenBy 链,其中 ThenBy 的数量可以变化

string[] sortParams = sortBy.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
listToBeSorted.OrderBy(i=>i.sortParams[0]).ThenBy(j=>j.sortParams[1]).ThenBy(k=>k.sortParams[2])...(so on till sortParams.length)

我该怎么做?另外,如何使用 sortOrder 参数在同一行中按升序或降序进行排序,而不是使用 if-else。

【问题讨论】:

  • 谷歌搜索“Dlinq”,它可以让你做.ThenBy(new string[])
  • 不是完全重复,但this answer 可能会给你一些启发。它使用枚举而不是字符串来定义搜索属性,但这应该不是问题。
  • 对于您的场景,似乎拥有一个属性名称字典和相应的 lambda 应该可以帮助您消除所有冗余。

标签: c# .net linq sorting


【解决方案1】:

您可以创建字典,将Func&lt;string, IComparable&gt; 映射到您的类的属性。

public class ItemWithProperty
{
    public string Property { get; set; }
}

public static void Main()
{
    Dictionary<string, Func<ItemWithProperty, IComparable>> stringPropertyMap = new Dictionary<string, Func<ItemWithProperty, IComparable>>()
    {
       {"param1", item => item.Property}
    };

    List<ItemWithProperty> toBeOrdered = new List<ItemWithProperty>();

    string[] parameters = {"param1"};
    var sorted = toBeOrdered.OrderBy(stringPropertyMap[parameters[0]]);
}

【讨论】:

  • 但是即使这样,我也必须为 sortParams 中的每个项目调用 stringPropertyMap.Add(),不是吗???
  • 您只需调用一次即可将您的类的 IComparable 属性映射到字符串值。然后您可以链接 orderby,然后使用您所说的参数。实际上你应该使用 IComparable 而不是 object - 编辑了我的答案。
  • @MrClan 实际上 Add() 在我的代码中是不必要的,因为我用值初始化了字典
【解决方案2】:

如果你改变你的方法签名,你会使用这个:

    private static IEnumerable<dynamic> ApplySort(IEnumerable<dynamic> listToBeSorted, ICollection<KeyValuePair<string, string>> sorters)
    {
        var orderBy = (sorters == null || sorters.Count == 0) ? new KeyValuePair<string, string>("createddate", "1") : sorters.First();
        var thenBys = (sorters == null || sorters.Count == 1) ? new List<KeyValuePair<string, string>>() : sorters.Except(Enumerable.Repeat(orderBy, 1));

        var orderedEnumerable = orderBy.Value == "1"
            ? listToBeSorted.OrderBy(x => GetPropertyValue(x, orderBy.Key))
            : listToBeSorted.OrderByDescending(x => GetPropertyValue(x, orderBy.Key));

        orderedEnumerable = thenBys.Aggregate(orderedEnumerable, (current, thenBy) => thenBy.Value == "1"
            ? current.ThenBy(x => GetPropertyValue(x, thenBy.Key))
            : current.ThenByDescending(x => GetPropertyValue(x, thenBy.Key)));

        return orderedEnumerable.ToList();
    }

    private static object GetPropertyValue(dynamic obj, string propName)
    {
        Type t = obj.GetType();
        return t.GetProperty(propName).GetValue(obj, null);
    }

试试:

    static void Main(string[] args)
    {
        var list = new List<dynamic>();
        list.Add(new { name = "Billy" });
        list.Add(new { name = "Johnny" });
        list.Add(new { name = "Ali" });

        var list2 = ApplySort(list, new List<KeyValuePair<string, string>>(new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("name", "1") }));

        foreach (var o in list2)
        {
            Console.WriteLine(o.name);
        }

        Console.ReadLine();

    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-16
    • 2020-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多