C# 按给定值对对象排序

C# 按给定值对对象排序,c#,linq,C#,Linq,鉴于: 哪个将使用上面的模板进行排序(因此具有Field1==“str1”的对象排在第一位,而不是具有“str2”等的对象)?在LINQ to Object中,使用: 在LINQ to对象中,使用: 我认为IndexOf可能在这里起作用: var ordered = list.OrderBy(x => Array.IndexOf(template, x.Field1)); 请注意,当对象根本不存在时,它将返回-1,这意味着它将首先出现。你得处理这个案子。如果您的字段保证在那里,那就没问题

鉴于:

哪个将使用上面的模板进行排序(因此具有
Field1==“str1”
的对象排在第一位,而不是具有
“str2”
等的对象)?

在LINQ to Object中,使用:

在LINQ to对象中,使用:


我认为
IndexOf
可能在这里起作用:

var ordered = list.OrderBy(x => Array.IndexOf(template, x.Field1));

请注意,当对象根本不存在时,它将返回-1,这意味着它将首先出现。你得处理这个案子。如果您的字段保证在那里,那就没问题。

我认为
IndexOf
可能在这里起作用:

var ordered = list.OrderBy(x => Array.IndexOf(template, x.Field1));

请注意,当对象根本不存在时,它将返回-1,这意味着它将首先出现。你得处理这个案子。如果您的字段保证在那里,那就没问题。

正如其他人所说,
Array.IndexOf
应该可以很好地完成这项工作。但是,如果
模板
很长,或者
列表
很长,则可能值得将
模板
转换为字典。比如:

list.OrderBy(_ => Array.IndexOf(template, _.Field1))
(或者你可以先创建一个字典,而不是数组——当你需要重新排序时,它更灵活)

这将为您提供一个从模板中键入字符串的字典,原始数组中的索引作为您的值。然后你可以这样分类:

var templateDict = template.Select((item,idx) => new { item, idx })
                           .ToDictionary(k => k.item, v => v.idx);
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));
}
因为字典中的查找是
O(1)
随着
template
list
的增长,它的伸缩性会更好


注:上述代码假定
字段1的所有值都存在于
模板中。如果不是,您将不得不处理
x.Field1
不在
templateDict
中的情况,正如其他人所说,
Array.IndexOf
应该可以很好地完成这项工作。但是,如果
模板
很长,或者
列表
很长,则可能值得将
模板
转换为字典。比如:

list.OrderBy(_ => Array.IndexOf(template, _.Field1))
var ordered = list.OrderBy(x => templateDict[x.Field1]);
(或者你可以先创建一个字典,而不是数组——当你需要重新排序时,它更灵活)

这将为您提供一个从模板中键入字符串的字典,原始数组中的索引作为您的值。然后你可以这样分类:

var templateDict = template.Select((item,idx) => new { item, idx })
                           .ToDictionary(k => k.item, v => v.idx);
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));
}
因为字典中的查找是
O(1)
随着
template
list
的增长,它的伸缩性会更好

注:上述代码假定
字段1的所有值都存在于
模板中。如果没有,则必须处理
x.Field1
不在
templateDict

var orderedList=list.OrderBy(d=>Array.IndexOf(template,d.machingcolumnfromtemplate)<0?int.MaxValue:Array.IndexOf(template,d.machingcolumnfromtemplate)).ToList();
var ordered = list.OrderBy(x => templateDict[x.Field1]);
var orderedList=list.OrderBy(d=>Array.IndexOf(template,d.machingcolumnfromtemplate)<0?int.MaxValue:Array.IndexOf(template,d.machingcolumnfromtemplate)).ToList();

实际上,我以前写过一个方法来实现这一点。以下是消息来源:

var orderedList = list.OrderBy(d => Array.IndexOf(template, d.MachingColumnFromTempalate) < 0 ? int.MaxValue : Array.IndexOf(template, d.MachingColumnFromTempalate)).ToList();

如果您想查看源代码、单元测试或它所在的库,可以。它也可以作为一个NuGet包提供。

实际上我以前已经编写了一个方法来实现这一点。以下是消息来源:

var orderedList = list.OrderBy(d => Array.IndexOf(template, d.MachingColumnFromTempalate) < 0 ? int.MaxValue : Array.IndexOf(template, d.MachingColumnFromTempalate)).ToList();

如果您想查看源代码、单元测试或它所在的库,可以。它也可以作为NuGet包提供。

这是更好的答案,因为您提到了缺少模板值的可能问题@RomanoZumbé虽然OP代码中的注释说,
表示Ljo.Field1的允许值以及顺序
,所以他们可能认为它是完整的。@MattBurland这是正确的,但我始终试图记住,其他人可能也会出现在这里。因此,解释还应涵盖可能的Pifall,并足够广泛,以适用于类似的问题。这是更好的答案,因为您提到了缺少模板值的可能问题@RomanoZumbé虽然OP代码中的注释说,
表示Ljo.Field1的允许值以及顺序
,所以他们可能认为它是完整的。@MattBurland这是正确的,但我始终试图记住,其他人可能也会出现在这里。所以这个解释也应该涵盖可能的pifalls,并且足够广泛,可以应用到类似的问题上。汉克斯·罗马诺,我正试图弄清楚,让它看起来像那样。XD您做了两次
IndexOf
?这是非常不必要的低效,我很确定这是移动所有这些项目的最后一个排序列表中没有在模板中,请告诉我,如果我是错的@我知道你为什么这么做。但它毫无必要地效率低下
IndexOf
O(n)
并且您要做两次。更糟糕的情况是,您必须搜索所有
模板
,以找到它是列表中的最后一项,然后再次搜索以获得实际索引,因为您将其丢弃。这是一个简单的修复:
varidx=Array.IndexOf(模板,d.Field1);返回idx<0?int.MaxValue:idx-只使用
IndexOf
一次。虽然此代码片段可以解决问题,但确实有助于提高文章质量。请记住,您将在将来回答读者的问题,这些人可能不知道您的代码建议的原因。还请尽量不要用解释性注释挤满你的代码,这会降低代码和解释的可读性!谢谢你,罗曼诺,我正想办法让它看起来像那样。XD您做了两次
IndexOf
?这是非常不必要的低效,我很确定这是移动所有这些项目的最后一个排序列表中没有在模板中,请告诉我,如果我是错的@我知道你为什么这么做。但它毫无必要地效率低下
IndexOf
O(n)
并且您要做两次。更糟糕的情况是,您必须搜索所有
模板