字典列表排序

6

我有一个字典值的集合(一个 List<>),需要对其进行排序。基本上,每个字典都是一行,而集合则是一页行。这里有一个简单的例子:

       var data = new List<Dictionary<string,string>>();
       data.Add(new Dictionary<string,string>() {
                    { "Firstname", "Bob"},
                    { "Lastname", "Banana"}
       });
       data.Add(new Dictionary<string, string>() {
                    { "Firstname", "Amy"},
                    { "Lastname", "Apple"}
       });
       data.Add(new Dictionary<string, string>() {
                    { "Firstname", "Charlie"},
                    { "Lastname", "Coconut"}
       });
       data.Add(new Dictionary<string, string>() {
                    { "Firstname", "Andy"},
                    { "Lastname", "Apple"}
       });

生成的排序字符串类似于“SQL”,例如:
 Lastname asc, Firstname desc

我已经在数据对象上尝试了.OrderBy(),但对于KeyValuePairs似乎不起作用。

有什么方法可以使用动态排序语句将数据列表按照这个顺序排序:

 Apple, Andy
 Apple, Amy
 Banana, Bob
 Coconut, Charlie

如果使用.NET 4.0,一些高级的LINQ可能会起作用。感谢任何建议。


你为什么要为每一行使用一个字典? - ChrisBint
2
顺便提一下,当您想要存储此类“对象”的“属性”时,应该创建一个类,而不是使用字典。 - BlueRaja - Danny Pflughoeft
“columns” 是由用户动态选择的。因此,每个用户可以选择不同的“columns”。 - CmdrTallen
4个回答

4
data.OrderBy(dict => dict["Lastname"])
    .ThenByDescending(dict => dict["Firstname"])

如果是动态的:
var sorted = data.OrderBy(item => 1); // identity (stable) sort
orderby = "Lastname asc, Firstname desc";
foreach (var key in orderby.Split(',').Select(clause => clause.Trim()))
{
    if (key.EndsWith(" desc", StringComparison.CurrentCultureIgnoreCase))
    {
        key = key.Substr(0, key.Length - 5);
        sorted = data.ThenByDescending(dict => dict[key]);
    } else
    {
        if (key.EndsWith(" asc", StringComparison.CurrentCultureIgnoreCase)) 
        {
            key = key.Substr(0, key.Length - 4);
        }
        sorted = data.ThenBy(dict => dict[key]);
    }
}

添加了一些示例代码(没有错误检查!),允许使用(可选的)ascdesc修饰符进行动态排序。_没有编译器看到过这段代码,因此它可能包含愚蠢的拼写错误_。 - sehe
+1,你比我先回答了...我一开始想得有点复杂了。 - agent-j

3
  private static IEnumerable<Dictionary<string, string>> Sort(IEnumerable<Dictionary<string,string>> data, string orderByString)
  {
     var orderBy =
        orderByString.Split(',').Select(
           s => s.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries))
           .Select (a => new {Field=a[0],Descending = "desc".Equals (a[1], StringComparison.InvariantCultureIgnoreCase)})
           .ToList ();
     if (orderBy.Count == 0)
        return data;
     // First one is OrderBy or OrderByDescending.
     IOrderedEnumerable<Dictionary<string, string>> ordered =
        orderBy[0].Descending ? data.OrderByDescending (d => d[orderBy[0].Field]) : data.OrderBy (d => d[orderBy[0].Field]);
     for (int i = 1; i < orderBy.Count; i++)
     {
        // Rest are ThenBy or ThenByDescending.
        var orderClause = orderBy[i];
        ordered =
           orderBy[i].Descending ? ordered.ThenByDescending(d => d[orderClause.Field]) : ordered.ThenBy(d => d[orderClause.Field]);
     }
     return ordered;
  }

已经有点更加动态了。关于不区分大小写的比较,你提出的观点很好 - 我会从你那里借鉴 :) - sehe
很棒的解决方案,尽管我只是在努力找到一种去除硬编码索引值的方法。 - Mrinal Kamboj
我使用了类型化数据实体List<SortInfo>,其中包含ColumnName和IsAscending列,而不是使用普通字符串来定义排序列、方向和顺序,因此我无需处理单个索引。 - Mrinal Kamboj

2

我不确定你是否有最佳实现,但由于上述只是一个示例而不是真实的情况,所以在这里提供给你:


(注:原文中的HTML标签已保留)
data
.OrderBy(x => x["Lastname"])
.ThenByDescending(x => x["Firstname"])
.Select(x => 
    new 
    { 
        Lastname = x["Lastname"],
        Firstname = x["Firstname"],
    });

0

除非你有特定的原因需要使用字典,但我想不出来,否则类似这样的东西会更好;

public class Person
{
    public string FirstName {get; set;}
    public string LastName  {get; set;}
}

var people = new [] {
    new Person { FirstName = "Amy", LastName = "Apple" },
    new Person { FirstName = "Andy", LastName = "Apple" },
    new Person { FirstName = "Charlie", LastName = "Coconut" } 
};

var sortedPeople = people
    .OrderBy(f => f.LastName)
    .ThenByDescending(f => f.FirstName);

根据评论更新:

请查看此帖子以获取可能的解决方案:

Entity Framework 4.1简单的动态表达式,用于object.property = value

具有多个/未知条件的动态Linq查询

(以上现已完全实施如下)

public static int UpdateSegment(int segmentId)
{
    Table<ContactViewItem> Contacts;
    var  conditionsFormatted = new Dictionary<string, string>();

    //Retrieve all conditions
    var segmentConditions = Lists.GetSegmentConditions(segmentId);

    //Iterate through conditions and process them
    foreach (var condition in segmentConditions)
    {
        switch (condition.Operator)
        {
            case SegmentCondition.OperatorType.Equals:
                condition.Condition =  string.Format("{1}=\"{0}\"", condition.Criteria, condition.Field);
                break;
            case SegmentCondition.OperatorType.Contains:
                condition.Condition = string.Format("{1}.Contains(\"{0}\")", condition.Criteria, condition.Field);
                break;
            default:
                throw new ApplicationException("Unexpected Operator for Condition");
        }
    }


    var db = new DbContext(ConfigurationManager.ConnectionStrings["c"].ConnectionString);

    var statusConditions = "Status = 1";

    var results = (IQueryable)db.Contacts.Where(statusConditions);

    var distinctFields = (from c in segmentConditions select c.Field).Distinct();

    foreach (var distinctField in distinctFields)
    {
        var values = (from s in segmentConditions where s.Field == distinctField select s.Condition).ToArray();
        var valuesJoined = string.Join("||", values);
        results = results.Where(valuesJoined);
    }

    results = results.Select("id");


    var xml = new StringBuilder();
    xml.Append("<Ids>");

    foreach (var id in results)
    {
        xml.Append(String.Format("<Id>{0}</Id>", id));
    }
    xml.Append("</Ids>");

    var idXml = XDocument.Parse(xml.ToString());

    return Lists.UpdateSegmentContacts(idXml.ToString(), segmentId);

}

可能有帮助,也可能没有!


不错的补充,但仍然不够动态;如果您使用像这样的非匿名类型,DLINQ 可能会应用。 - sehe
哇...实体框架,数据库连接字符串,表,Linq to XML,系统超载!这一定是非常复杂的东西;但我已经看不到整个情况了 :) 另一方面,也许原帖作者正在从类似的数据源获取数据,可能并没有像乍一看那么复杂? - sehe
请优先使用.AsQueryable()扩展方法而不是(IQueryable)强制转换;该扩展方法可以更好地推导表达式并提供更多的编译时检查。 - sehe
确实,但它解决了一个特定的问题。等我开始处理技术债务时,会尽可能重构代码。现在测试通过了! - ChrisBint
你确定你没有粘贴错误的代码片段吗?我看不到 XML 是从哪里来的,也不知道 UpdateSegment 函数应该如何调用... - sehe
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接