Sort List of Dictionaries
I have a collection of dictionary values (a List<>
) that I need to sort. Basically each dictionary is a 'row' and the collection is a page of rows. A simple example;
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"}
});
The sort string generated is "SQL" like, example
Lastname asc, Firstname desc
I have t开发者_StackOverflow社区ried .OrderBy()
on the data object but that doesn't seem to work right against the KeyValuePairs.
Any idea how I could get the data list to be sorted to be in this order, using the dynamic sort statement:
Apple, Andy
Apple, Amy
Banana, Bob
Coconut, Charlie
Using .NET 4.0 if some fancy LINQ will work. Thanks for any suggestions.
data.OrderBy(dict => dict["Lastname"])
.ThenByDescending(dict => dict["Firstname"])
If it's dynamic:
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]);
}
}
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;
}
I'm not sure if you have the best implementation here, but as the above is an example and not a real world case, here you go:
data
.OrderBy(x => x["Lastname"])
.ThenByDescending(x => x["Firstname"])
.Select(x =>
new
{
Lastname = x["Lastname"],
Firstname = x["Firstname"],
});
Unless there is a specific reason you need to use Dictionaries, for which I can't think, something similiar to this would be better;
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);
Updated based on comments;
See this post for a possible solution;
Entity Framework 4.1 simple dynamic expression for object.property = value
and
Dynamic linq query with multiple/unknown criteria
(the above has now been fully implemented as below)
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);
}
Might help, then again, might not!
精彩评论