Pregunta

Tengo una página que muestra dos objetos y luego el usuario elige uno de estos. Grabo la preferencia y la combinación en una base de datos MSSQL y termino almacenando datos como este:

UserId=1, BetterObjectId=1, WorseObjectId=2

Ahora me gustaría evitar mostrar esa combinación de objetos (1,2 / 2,1) nunca más.

Entonces, ¿cómo genero combinaciones aleatorias para mostrar al usuario excluyendo las combinaciones vistas anteriormente?

Parece que debería ser una pregunta muy directa, pero como la mayoría de los programadores, me falta sueño y café, así que su ayuda es muy apreciada :-)

El enfoque ingenuo muy es algo como esto (y todas las llamadas a esta función tendrían que estar envueltas en un cheque para ver si el usuario ya ha calificado tantas veces como nCr donde n es el recuento de elementos yr es 2):

public List<Item> GetTwoRandomItems(int userId)
{
    Item i = null, i2 = null;
    List<Item> r = null;

    while (i == null || i2 == null)
    {
        r = GetTwoRandomItemsRaw();
        i = r[0];
        i2 = r[1];
        if (GetRating(i.Id, i2.Id, userId) != null) /* Checks if viewed */
        {
            i = null;
            i2 = null;
        }
    }
    return r;
}

private List<Item> GetTwoRandomItemsRaw()
{
    return Items.ToList().OrderBy(i => Guid.NewGuid()).Take(2).ToList();
}

Edits

Usando algunos SQL, puedo generar una lista de todos los elementos que no están completos (es decir, hay una combinación que involucra el elemento que el usuario no ha visto) pero no creo que sea particularmente útil.

También puedo imaginar generar todas las combinaciones posibles y eliminar las ya vistas antes de elegir 2 elementos aleatorios, pero esta es otra solución terrible.

Una posibilidad (uso intensivo de memoria para n grande) es generar todas las combinaciones posibles y almacenar el ID de combinación en la clasificación. Entonces puedo hacer una SELECCIÓN de todas las combinaciones DONDE combineId NO ESTÁ EN (SELECCIONAR combinaciónId de calificaciones DONDE userId = x) con algunos cambios para reflejar la relación simétrica de las combinaciones.

¿Fue útil?

Solución

Table Item: ItemId
Table Rating: UserId, ItemId1, ItemId2, WinnerId

Si necesita ese ItemId1 < ItemId2 en la tabla de clasificación, solo tiene que verificar la tabla de clasificación una vez.

var pair = db.Items.Join(db.Items,
  i1 => i1.ItemId,
  i2 => i2.ItemId,
  (i1, i2) => new {i1, i2}
)  //produce all pairs
.Where(x => x.i1.ItemId < x.i2.ItemId) //filter diagonal to unique pairs
.Where(x => 
  !db.Ratings
  .Where(r => r.UserId == userId
    && r.ItemId1 == x.i1.ItemId
    && r.ItemId2 == x.i2.ItemId)
  .Any() //not any ratings for this user and pair
)
.OrderBy(x => db.GetNewId()) //in-database random ordering
.First();  // just give me the first one

return new List<Item>() {pair.i1, pair.i2 };

Aquí hay un blog sobre obtener " random " traducido a la base de datos.

Otros consejos

Una solución es esta:

SELECT TOP 1 i.id item1, i2.id item2 from item i, item i2 
WHERE i.id <> i2.id 
AND (SELECT COUNT(*) FROM Rating WHERE userId=@userId AND FK_ItemBetter=i.id AND FK_ItemWorse=i2.id) = 0
AND (SELECT COUNT(*) FROM Rating WHERE userId=@userId AND FK_ItemBetter=i2.id AND FK_ItemWorse=i.id) = 0
ORDER BY NEWID()

No conocía el < em> método de combinación cruzada de simplemente enumerar varias tablas FROM antes.

Suponiendo que la lista de elementos disponibles está en la base de datos, manejaría este problema completamente en la base de datos. Ya está llegando a la base de datos, pase lo que pase, ¿por qué no hacerlo allí?

¿Qué pasa con poner todos los objetos en una cola o una pila, y luego hacer estallar 2 y 2 hasta que estén vacíos?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top