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 you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With