Pergunta

Eu tenho uma página que exibe dois objetos e, em seguida, o usuário escolhe um deles. Eu gravo a preferência ea combinação em um banco de dados MSSQL e acabam armazenando dados como este:

UserId=1, BetterObjectId=1, WorseObjectId=2

Agora eu gostaria de evitar mostrando que a combinação dos objetos (1,2 / 2,1) nunca mais.

Então, como faço para gerar combinações aleatórias para mostrar ao usuário excluindo combinações previamente vistas?

Isto parece que deve ser uma pergunta muito simples, mas como a maioria dos programadores que eu sou curto no sono e café para a sua ajuda é muito apreciada: -)

O próprio abordagem ingênuo é algo como isto (e todas as chamadas para esta função teria que ser envolto em uma verificação para ver se o usuário já tenha avaliado como muitas vezes como nCr onde n é o contagem item e r é 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();
}

Edições

Usando algumas SQL I pode gerar uma lista de todos os itens que não são completo (ou seja, há uma combinação envolvendo o item que o usuário não tenha visto), mas eu não acho que é particularmente útil.

Eu também posso imaginar gerar todas as combinações possíveis e eliminando os já vistos antes de escolher 2 itens aleatórios, mas esta é uma outra solução terrível.

A possibilidade (memória intensivo para grande n) é gerar todas as combinações possíveis e armazenar o combinationId na classificação. Então eu posso apenas fazer um SELECT de todas as combinações ONDE combinationId NÃO ESTÁ EM (SELECT combinationId DE classificações ONDE userId = x), com algumas alterações para refletir a relação simétrica de combinações.

Foi útil?

Solução

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

Se você precisar que ItemId1

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

Aqui está um blogue sobre a obtenção de "random" traduzido para o banco de dados.

Outras dicas

Uma solução é a seguinte:

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

Eu não estava ciente do < em> junção cruzada método de apenas de listagem múltipla de tabelas antes.

Assumindo que a lista de itens disponíveis é no banco de dados, eu iria lidar com este problema inteiramente no banco de dados. Você está batendo no banco de dados já, não importa o quê, então por que não fazê-lo lá?

O que sobre a colocação de todos os objetos em uma fila ou uma pilha, e em seguida, pop 2 e 2 fora até que eles estão vazios?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top