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 with the same order as the first. How can I reorder the product list so that the result is: "BBB", "HHH", "ZZZ"?

EDIT: Changed code property to public as mentioned by @juharr

+3


source to share


5 answers


You would use IndexOf

:

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

      



The only catch in this case is if there is something in the second list not in the first list, then they will go to the beginning of the second list.

The MSDN post on IndexOf

can be found here .

+3


source


You can try something like this

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

      

if it's the same object Product

. Otherwise, you can try something like:

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

      



and write a little helper method GetIndex

. Or create an extension method Index()

for List<>

which will give

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

      

The method is GetIndex

pretty simple, so I am omitting it here.

(I don't have a PC to run the code, so please excuse the small bugs)

+2


source


Here's an efficient way to do it:

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 runtime complexity of O (N log N), whereas the approach using IndexOf()

would be O (N 2 ).

This assumes the following:

  • in sourceProductsOrder

    no recurring product codes.
  • sourceProductsOrder

    contains all product codes in products
  • you are creating Code

    field / property not private

If necessary, you can create protection against the first bullet by replacing it with the following expression:

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

      

You can specify a second brand by replacing the second statement with the following:

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

      

And you can use both if you need to. This will slow down the algorithm a bit, but it should continue to run at O โ€‹โ€‹(N log N) even with these changes.

+1


source


I would implement a compare function that searches the order of sourceProductsOrder

from using a hash table. The lookup table would look like

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

      

Then you can compare the order of the two elements and make it simple <

(pseudocode):

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

      

Building a hash table would be linear, and doing a view would normally be nlogn

0


source


Easy transition:

IEnumerable<Product> result = 

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

      

This will give the desired result. Objects with ProductCodes that are not available in the source list will be placed at the beginning of the result set. This will work fine for a few hundred subjects I guess.

If you have to deal with thousands of objects than an answer like @ Jon's will likely work better. There you first create a search / grade view for each item and then use it to sort / order.

The approach I described is O (n2).

0


source







All Articles