假设:
class C
{
public string Field1;
public string Field2;
}
template = new [] { "str1", "str2", ... }.ToList() // presents allowed values for C.Field1 as well as order
list = new List<C> { ob1, ob2, ... }
问题:
我如何执行Linq的
list.OrderBy(x => x.Field1)
将使用上面的模板作为订单(因此Field1 == "str1"
的对象首先出现,而"str2"
的对象等等)?
答案 0 :(得分:2)
在LINQ to Object中,使用Array.IndexOf
:
var ordered = list
.Select(x => new { Obj = x, Index = Array.IndexOf(template, x.Field1)})
.OrderBy(p => p.Index < 0 ? 1 : 0) // Items with missing text go to the end
.ThenBy(p => p.Index) // The actual ordering happens here
.Select(p => p.Obj); // Drop the index from the result
这不适用于EF或LINQ to SQL,因此您需要将对象放入内存进行排序。
注意:以上假设列表不详尽无遗。如果是,则更简单的查询就足够了:
var ordered = list.OrderBy(x => Array.IndexOf(template, x.Field1));
答案 1 :(得分:1)
我认为IndexOf
可能会在这里工作:
list.OrderBy(_ => Array.IndexOf(template, _.Field1))
请注意,当对象根本不存在时,它将返回-1,这意味着它将首先出现。你必须处理这个案子。如果保证你的领域在那里,那很好。
答案 2 :(得分:1)
正如其他人所说,Array.IndexOf
应该做得很好。但是,如果template
很长或list
很长,那么将template
转换为字典可能是值得的。类似的东西:
var templateDict = template.Select((item,idx) => new { item, idx })
.ToDictionary(k => k.item, v => v.idx);
(或者你可以先从一开始创建一个字典而不是一个数组 - 当你需要重新排序东西时它会更灵活)
这将为您提供一个从模板中键入字符串的字典,其中原始数组中的索引作为您的值。然后你可以像这样排序:
var ordered = list.OrderBy(x => templateDict[x.Field1]);
其中,因为字典O(1)
中的查找会随着template
和list
的增长而更好地扩展。
注意:上述代码假定Field1
中存在template
的所有值。如果不是,则必须处理x.Field1
不在templateDict
的情况。
答案 3 :(得分:0)
var orderedList = list.OrderBy(d => Array.IndexOf(template, d.MachingColumnFromTempalate) < 0 ? int.MaxValue : Array.IndexOf(template, d.MachingColumnFromTempalate)).ToList();
答案 4 :(得分:0)
我之前已经写过一个方法来做这件事。这是来源:
public static IOrderedEnumerable<T> OrderToMatch<T, TKey>(this IEnumerable<T> source, Func<T, TKey> sortKeySelector, IEnumerable<TKey> ordering)
{
var orderLookup = ordering
.Select((x, i) => new { key = x, index = i })
.ToDictionary(k => k.key, v => v.index);
if (!orderLookup.Any())
{
throw new ArgumentException("Ordering collection cannot be empty.", nameof(ordering));
}
T[] sourceArray = source.ToArray();
return sourceArray
.OrderBy(x =>
{
int index;
if (orderLookup.TryGetValue(sortKeySelector(x), out index))
{
return index;
}
return Int32.MaxValue;
})
.ThenBy(x => Array.IndexOf(sourceArray, x));
}
你可以像这样使用它:
var ordered = list.OrderToMatch(x => x.Field1, template);
如果您想查看来源,单元测试或其所在的库,您可以find it on GitHub。它也可以作为NuGet包使用。