根据另一个对象对列表进行排序

Sorting a list of objects based on another

public class Product
{
    public string Code { get; private set; }

    public Product(string code)
    {
        Code = code;
    }
}

List<Product> sourceProductsOrder = 
              new List<Product>() { new Product("BBB"), new Product("QQQ"), 
                                    new Product("FFF"), new Product("HHH"),
                                    new Product("PPP"), new Product("ZZZ")};

List<Product> products = 
              new List<Product>() { new Product("ZZZ"), new Product("BBB"),
                                    new Product("HHH")};

我有两个产品列表,我想以与第一个相同的顺序重新排序第二个。 我如何重新排序产品列表,以便结果为:"BBB"、"HHH"、"ZZZ"?

编辑: 将代码属性更改为 public,如@juharr 所述

你可以试试这个

products.OrderBy(p => sourceProductsOrder.IndexOf(p))

如果是同一个Product对象。否则,您可以尝试类似的操作:

products.OrderBy(p => GetIndex(sourceProductsOrder, p))

并编写一个小的 GetIndex 辅助方法。或者为 List<> 创建一个 Index() 扩展方法,这将产生

products.OrderBy(p => sourceProductsOrder.Index(p))

GetIndex方法比较简单,这里省略

(我没有 PC 运行 代码所以请原谅小错误)

你会使用 IndexOf:

var sourceCodes = sourceProductsOrder.Select(s => s.Code).ToList();
products = products.OrderBy(p => sourceCodes.IndexOf(p.Code));

唯一的问题是,如果第二个列表中有第一个列表中没有的内容,这些内容将转到第二个列表的开头。

IndexOf 上的 MSDN post 可以找到 here

我将实现一个比较函数,该函数使用散列 table 查找来自 sourceProductsOrder 的订单。查找 table 看起来像

(key) : (value)
"BBB" : 1
"QQQ" : 2
"FFF" : 3
"HHH" : 4
"PPP" : 5
"ZZZ" : 6

然后您的比较可以查找两个元素的顺序并执行简单的 <(伪代码):

int compareFunction(Product a, Product b){ 
    return lookupTable[a] < lookupTable[b]
}

构建哈希 [​​=21=] 是线性的,排序通常是 nlogn

这是一种有效的方法:

var lookup = sourceProductsOrder.Select((p, i) => new { p.Code, i })
                                .ToDictionary(x => x.Code, x => x.i);

products = products.OrderBy(p => lookup[p.Code]).ToList();

这应该具有 O(N log N) 的 运行 时间复杂度,而使用 IndexOf() 的方法将是 O(N2) .

假设如下:

  • sourceProductsOrder
  • 中没有重复的产品代码
  • sourceProductsOrder包含products
  • 中的所有产品代码
  • 您将 Code field/property 设为非私有

如果需要,您可以通过将第一个语句替换为以下内容来创建针对第一个项目符号的保​​护措施:

var lookup = sourceProductsOrder.GroupBy(p => p.Code)
                                .Select((g, i) => new { g.Key, i })
                                .ToDictionary(x => x.Key, x => x.i);

您可以通过将第二个语句替换为以下内容来说明第二个项目符号:

products = products.OrderBy(p => 
            lookup.ContainsKey(p.Code) ?  lookup[p.Code] : Int32.MaxValue).ToList();

如果需要,您可以同时使用两者。这些会减慢算法一点点,但它应该继续有一个 O(N log N) 运行 时间,即使有这些改变。

来得容易去得也快:

IEnumerable<Product> result = 

products.OrderBy(p => sourceProductsOrder.IndexOf(sourceProductsOrder.FirstOrDefault(p2 => p2.Code == p.Code)));

这将提供所需的结果。 ProductCodes 在源列表中不可用的对象将被放置在结果集的开头。我想这对几百个项目来说效果很好。

如果您必须处理数千个对象,那么像@Jon 这样的答案可能会表现得更好。在那里,您首先为每个项目创建一种查找值/分数,然后将其用于排序/排序。

我描述的方法是 O(n2)。