简体   繁体   English

基于另一个对象列表排序

[英]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")};

I have two product lists and I want to reorder the second one with the same order as the first. 我有两个产品清单,我想以与第一个相同的顺序重新排序第二个产品清单。 How can I reorder the products list so that the result would be : "BBB", "HHH", "ZZZ"? 如何重新排序产品列表,以便结果为:“BBB”,“HHH”,“ZZZ”?

EDIT: Changed Code property to public as @juharr mentioned 编辑: @juharr提到将代码属性更改为公共

You would use IndexOf : 你会使用IndexOf

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

The only catch to this is if the second list has something not in the first list those will go to the beginning of the second list. 唯一的问题是,如果第二个列表中的某些内容不在第一个列表中,那么它们将转到第二个列表的开头。

MSDN post on IndexOf can be found here . 关于IndexOf MSDN帖子可以在这里找到。

You could try something like this 你可以尝试这样的事情

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

if it is the same Product object. 如果它是相同的Product对象。 Otherwise, you could try something like: 否则,您可以尝试以下方法:

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

and write a small GetIndex helper method. 并编写一个小的GetIndex辅助方法。 Or create a Index() extension method for List<> , which would yield 或者为List<>创建一个Index()扩展方法,它将产生

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

The GetIndex method is rather simple so I omit it here. GetIndex方法相当简单,所以我在这里省略它。

(I have no PC to run the code so please excuse small errors) (我没有PC来运行代码所以请原谅小错误)

Here is an efficient way to do this: 这是一种有效的方法:

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();

This should have a running time complexity of O(N log N), whereas an approach using IndexOf() would be O(N 2 ). 这应该具有O(N log N)的运行时复杂度,而使用IndexOf()的方法将是O(N 2 )。

This assumes the following: 这假设如下:

  • there are no duplicate product codes in sourceProductsOrder sourceProductsOrder中没有重复的产品代码
  • sourceProductsOrder contains all of the product codes in products sourceProductsOrder包含产品中的所有产品代码
  • you make the Code field/property non-private 您使Code字段/属性非私有

If needed, you can create a safeguard against the first bullet by replacing the first statement with this: 如果需要,您可以使用以下内容替换第一个语句来创建针对第一个项目符号的安全措施:

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

You can account for the second bullet by replacing the second statement with this: 您可以通过将第二个语句替换为以下内容来解释第二个项目:

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

And you can use both if you need to. 如果需要,你可以使用两者。 These will slow down the algorithm a bit, but it should continue to have an O(N log N) running time even with these alterations. 这些会使算法慢一点,但即使有这些改动,它也应该继续具有O(N log N)运行时间。

I would implement a compare function that does a lookup of the order from sourceProductsOrder using a hash table. 我将实现一个比较函数,它使用哈希表从sourceProductsOrder查找顺序。 The lookup table would look like 查找表看起来像

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

Your compare could then lookup the order of the two elements and do a simple < (pseudo code): 然后你的比较可以查找两个元素的顺序并做一个简单的< (伪代码):

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

Building the hash table would be linear and doing the sort would generally be nlogn 构建哈希表将是线性的,并且进行排序通常是nlogn

Easy come easy go: 来得便当去得快:

IEnumerable<Product> result = 

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

This will provide the desired result. 这将提供所需的结果。 Objects with ProductCodes not available in the source list will be placed at the beginning of the resultset. 源列表中没有ProductCodes的对象将放在结果集的开头。 This will perform just fine for a couple of hundred of items I suppose. 对于我想的几百个项目,这将表现得很好。

If you have to deal with thousands of objects than an answer like @Jon's will likely perform better. 如果你必须处理成千上万的对象而不是像@ Jon那样的答案可能会表现得更好。 There you first create a kind of lookup value / score for each item and then use that for sorting / ordering. 在那里,您首先为每个项目创建一种查找值/分数,然后将其用于排序/排序。

The approach I described is O(n2). 我描述的方法是O(n2)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM