2010-03-19 16 views
5

Mi OM tiene un objeto 'producto'.
Cada producto tiene una 'identificación del fabricante' (propiedad, número entero).
Cuando tengo una lista de productos para mostrar, los tres primeros se muestran como los "productos presentados".
La lista ya está ordenada en un orden de clasificación específico, colocando los productos 'destacados' en primer lugar en la lista.Clasificación LINQ: los tres primeros deben ser fabricantes diferentes

Sin embargo, ahora debo asegurarme de que los productos presentados en el listado son de diferentes fabricantes. Quiero tener un método para llamar para hacer esta nueva clasificación. Tratando de utilizar LINQ a la consulta de los 'productos' de entrada y el 'resultados'

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; 

} 

Gracias de antemano.

Aclaración:
La lista original está en un orden específico: el 'mejor vendido', el más alto primero (orden descendente). La lista resultante debe permanecer en este orden, con la excepción de ajustar o mover los elementos 'hacia arriba' para que los diferentes fabricantes se vean las principales características de n.

Si el primer n (numberOfFeatures) de los artículos tiene fabricantes diferentes, entonces el listado no necesita ser alterado en absoluto.
p. Si numberOfFeatures = 3
Producto 1 - Fabricante A (primera función)
Producto 2 - Fabricante B (segunda función)
Producto 3 - Fabricante C (tercera función)
Producto 4 - Fabricante A (... Sin marcar ...)
Producto 5 - Fabricante A (... no verificado ...)

Por ej. Caso para ajustar ... por ejemplo ... ENTRADA Lista
Producto 1 - Fabricante Un
Producto 2 - Fabricante Un
Producto 3 - Fabricante B
Producto 4 - Fabricante Un
Producto 5 - Fabricante F
(... nos gustaría ...)
Producto 1 - Fabricante A (primera función)
Producto 3 - Fabricante B (segunda función ... mueve hacia arriba)
Producto 5 - Fabricante F (tercera función. .. movido hacia arriba)
Prod UCT 2 - Fabricante A (... empuja hacia abajo la lista ...)
Producto 4 - Fabricante A (... empuja hacia abajo la lista ...)

Respuesta

3

Creo Bryan encapsula la lógica de clasificación bastante bien y no es algo que se me ocurrió hacer. Me gustaría presentar mi opinión de todos modos usando un ejemplo de Foo.

 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 siendo

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

Se obtiene (Baz) 1, 3, 6 y desplaza a la parte superior, la que queda en su orden original después.

+0

Anthony, gracias. Esto parece haberme llevado a donde tengo que estar. Voy a hacer un poco más de pruebas a/b, pero se ve bien hasta ahora. gracias – Rob

+0

Existen algunos buenos procesos de pensamiento en cada una de estas respuestas. Me alegra contribuir con ellos. –

+0

+1 enfoque interesante. Se lee bien. Optimicé para la menor cantidad de iteraciones y comparaciones, lo que me llevó a pensar en cosas a nivel de comparación individual. Su enfoque enmarca la solución como una consulta, que es posiblemente más clara y vale la pena el costo O (3N). –

2

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

Editar: La pregunta es realmente acerca de un pedido personalizado de una lista. He elegido para describir la comparación en sí, y usar eso para ordenar:

return products 
    .OrderBy(product => product, new FeaturedProductComparer(numberOfFeatures)) 
    .ToList(); 

Esto se hace mediante la aplicación de IComparer<Product> y mantener el seguimiento de los fabricantes que se han encontrado.Cuando hay menos de 3 y nos encontramos con una nueva, estamos a favor de que el producto:

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; 
     } 
    } 
} 
+0

No creo que eso garantice que el resto de los registros se mantendrá en el mismo orden, simplemente toma el primer producto de cada fabricante. – R0MANARMY

+0

Tienes razón, me perdí ese requisito. Edición. –

+0

Y esta solución es mejor que simplemente recorrer los productos y dividirlos en dos cubos (luego volver a unirlos) o ¿está tratando de ver si hay alguna manera de hacer que Linq haga su oferta? – R0MANARMY

0

Si he entendido bien creo que esto va a funcionar para usted.

¿Está preguntando cuáles son las primeras "numberOfFeatures" de "Product" s que tienen diferentes "ManufacturerId" s que usan la lista ordenada de "productos"?

public List<Product> SetFeatures(List<Product> products, int numberOfFeatures) 
{ 
    return products 
     .GroupBy(p => p.ManufacturerId) 
     .Take(numberOfFeatures) 
     .Select(g => g.First()); 
} 
+1

No, eso es lo que pensé originalmente también. Ver los comentarios en mi respuesta para aclaración. –

1

Editar resulta Distinct() no se requiere, el código revisado:

¿Es esto lo que quieres?

var result = 
     products 
      .GroupBy(x => x.Id) 
      .Take(numberOfFeatures) 
      .Select(x => x.First()) 
      .Union(products); 
return result.ToList(); 

Tenga en cuenta que GroupBy tendrá la correcta sequence y Unión establecerá also

+0

Esto no funcionará porque GroupBy cambia el orden de los productos, es igual a la solución de Jason y Bryan Watt, y no funciona de la misma manera. – R0MANARMY

+0

@ROMANARMY Sí, pero el pedido posterior de los productos (que la unión se haya realizado) no habrá cambiado y el pedido será correcto. El grupo por solo se usa para los productos _featured_. Intente que muestre datos para verlo usted mismo. – Cornelius

+0

¿Existe alguna garantía de que 'Distinct()' elija el primer miembro destacado de la lista y no el que está más abajo? – Gabe

Cuestiones relacionadas