LINQ Sorting - First three need to be different manufacturers
My OM has a 'product' object.
Each product has a 'manufacturer id' (property, integer). When I have a list of products to display, the first three are displayed as the 'featured products'. The list is already sorted in a specific sort order, putting the 'featured' products first in the list.However, I now need to ensure the featured products in the listing are from different Manufacturers. I want to have a method to call to do this re-sorting. Trying to utilize LINQ to to the querying of the input 'products' and the 'results'
public List<Product> SetFeatures(List<Product> products, int numberOfFeatures)
{
List<Product> result;
// ensure the 2nd product is different manufacturer than the first ....
// ensure the 3rd product is a different manufacturer than the first two...
// ... etc ... for the numberOfFeatures
return result;
}
Thanks in advance.
Clarification:
The original list is in a specific order: the 'best selling', highest开发者_高级运维 first (descending order). The resulting list should remain in this order with the exception of adjusting or moving 'up' items so that the differing manufacturers are seen the top n features.If the first n (numberOfFeatures) of items all have different manufacturers, then the listing does not need to be altered at all.
e.g. If numberOfFeatures = 3 Product 1 - Manufacturer A (1st feature) Product 2 - Manufacturer B (2nd feature) Product 3 - Manufacturer C (3rd feature) Product 4 - Manufacturer A (...not checked...) Product 5 - Manufacturer A (...not checked...)e.g. Case to adjust ... for example ... INPUT List
Product 1 - Manufacturer A Product 2 - Manufacturer A Product 3 - Manufacturer B Product 4 - Manufacturer A Product 5 - Manufacturer F (... we would want ...) Product 1 - Manufacturer A (1st feature) Product 3 - Manufacturer B (2nd feature ... moved up) Product 5 - Manufacturer F (3rd feature ... moved up) Product 2 - Manufacturer A (...pushed down the list...) Product 4 - Manufacturer A (...pushed down the list...)I think Bryan encapsulated the sorting logic pretty well and it's not something I thought of doing. I'd like to present my take on it anyway using a Foo example.
List<Foo> foos = new List<Foo>()
{
new Foo() { Baz = 1, Blah = "A"},
new Foo() { Baz = 2, Blah = "A"},
new Foo() { Baz = 3, Blah = "B"},
new Foo() { Baz = 4, Blah = "B"},
new Foo() { Baz = 5, Blah = "B"},
new Foo() { Baz = 6, Blah = "C"},
new Foo() { Baz = 7, Blah = "C"},
new Foo() { Baz = 8, Blah = "D"},
new Foo() { Baz = 9, Blah = "A"},
new Foo() { Baz = 10, Blah = "B"},
};
var query = foos.Distinct(new FooComparer()).Take(3).ToList();
var theRest = foos.Except(query);
query.AddRange(theRest);
FooComparer being
public class FooComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
return x.Blah == y.Blah;
}
public int GetHashCode(Foo obj)
{
return obj.Blah.GetHashCode();
}
}
You get (Baz) 1, 3, and 6 shifted to top, the remaining in their original order afterwards.
public List SetFeatures(List products, int numberOfFeatures) { var manufacturerProducts = from product in products group product by product.ManufacturerId into productGroup select productGroup.First(); return manufacturerProducts.Take(numberOfFeatures).ToList(); }
Edit: The question is really about a custom ordering of a list. I chose to describe the comparison itself, and use that to sort:
return products
.OrderBy(product => product, new FeaturedProductComparer(numberOfFeatures))
.ToList();
This is done by implementing IComparer<Product>
and keeping tracking of the manufacturers that have been encountered. When there are less than 3 and we find a new one, we favor that product:
private class FeaturedProductComparer : IComparer<Product>
{
// OrderBy preserves the order of equal elements
private const int _originalOrder = 0;
private const int _xFirst = -1;
private const int _yFirst = 1;
private readonly HashSet<int> _manufacturerIds = new HashSet<int>();
private readonly int _numberOfFeatures;
internal FeaturedProductComparer(int numberOfFeatures)
{
_numberOfFeatures = numberOfFeatures;
}
public int Compare(Product x, Product y)
{
return _manufacturerIds.Count == _numberOfFeatures
? _originalOrder
: CompareManufacturer(x, y);
}
private int CompareManufacturer(Product x, Product y)
{
if(!_manufacturerIds.Contains(x.ManufacturerId))
{
_manufacturerIds.Add(x.ManufacturerId);
// Sort existing featured products ahead of new ones
return _manufacturerIds.Contains(y.ManufacturerId) ? _yFirst : _xFirst;
}
else if(!_manufacturerIds.Contains(y.ManufacturerId))
{
_manufacturerIds.Add(y.ManufacturerId);
// Sort existing featured products ahead of new ones
return _manufacturerIds.Contains(x.ManufacturerId) ? _xFirst : _yFirst;
}
else
{
return _originalOrder;
}
}
}
Edit turns out Distinct()
is not required, the revised code:
Is this what you want?
var result =
products
.GroupBy(x => x.Id)
.Take(numberOfFeatures)
.Select(x => x.First())
.Union(products);
return result.ToList();
Note that GroupBy will have the correct sequence and Union will also
If I understand you correctly I think this will work for you.
Are you asking what are the first "numberOfFeatures" of "Product"s that have different "ManufacturerId"s using the ordered list "products"?
public List<Product> SetFeatures(List<Product> products, int numberOfFeatures)
{
return products
.GroupBy(p => p.ManufacturerId)
.Take(numberOfFeatures)
.Select(g => g.First());
}
精彩评论