按给定值排序对象

时间:2017-07-13 12:37:15

标签: c# linq

假设:

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"的对象等等)?

5 个答案:

答案 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)中的查找会随着templatelist的增长而更好地扩展。

注意:上述代码假定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包使用。