2009-03-02 5 views
4

У меня есть страница, которая отображает два объекта, а затем пользователь выбирает один из них. Я записываю предпочтения и сочетание в базе данных MSSQL и в конечном итоге хранения данных, как это:Найдите комбинацию из двух элементов, которые не были просмотрены вместе (LINQ, SQL или C#)

UserId=1, BetterObjectId=1, WorseObjectId=2 

Теперь я хотел бы, чтобы не показывать, что сочетание объектов (1,2/2,1) когда-либо снова.

Как я могу создать случайные комбинации, чтобы показать пользователя, исключая ранее просмотренные комбинации?

Это кажется, что это должно быть очень простой вопрос, но, как и большинство программистов, я мало сна и кофе, так ваша помощь ценится :-)

очень наивный подход является чем-то вроде это (и все вызовы этой функции должны быть обернуты в проверке, чтобы увидеть, если пользователь уже оценили, как много раз, как Ncr где п есть число элементов и г 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(); 
} 

Редактирует

Используя некоторый SQL, я могу сгенерировать список всех элементов, которые не являются полными (т. есть комбинация с элементом, который пользователь не видел), но я не думаю, что это особенно полезно.

Я также могу представить себе любую возможную комбинацию и исключение уже просмотренных до выбора 2 случайных предметов, но это еще одно страшное решение.

Возможность (интенсивная память для больших n) - генерировать все возможные комбинации и сохранять комбинацию в рейтинге. Тогда я могу просто сделать SELECT всех комбинаций. WHERE combinationId IS NOT IN (SELECT combId FROM ratings WHERE userId = x) с некоторыми изменениями, отражающими симметричное соотношение комбинаций.

ответ

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

Если требуется, ItemId1 < ItemId2 в рейтинговой таблице, вам нужно только проверить рейтинг таблицы один раз.

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

Вот blog о получении «случайных» переведены в базу данных.

+0

Спасибо за вход! Я задавался вопросом о сохранении победителя отдельно - на данный момент это был WinnerId/LoserId, а не Item1, Item2, Winner. –

+0

Это компромисс между пространством и способностью к порядку. –

0

Предполагая, что список доступных элементов находится в базе данных, я бы полностью справился с этой проблемой в базе данных. Вы бьете базу данных уже, несмотря ни на что, так почему бы не сделать это там?

+0

Да, именно поэтому у меня был SQL как один из возможных методов решения. –

0

Как насчет того, чтобы поместить все объекты в очередь или в стек, а затем сбросить 2 и 2 до тех пор, пока они не будут пустыми?

+0

Вы сохранили бы стек для каждого пользователя? Не уверен, что это даст любую комбинацию. –

+0

О, это веб-материал и через сеансы?я просто думал простой C#. Не принесли бы всех возможных комбинаций, но думали, что одна из целей не видит объект дважды. Что будет в моем решении. Если веб-сайт и между сессиями, sql, вероятно, способ пойти =) – Svish

+0

Ах, извините, пренебречь частью сеансов. Да, твоя идея хороша для остановки объектов дважды - мне просто нужно было что-то, что включало каждую комбинацию. Спасибо, хотя :-) –

1

Одно из решений заключается в следующем:

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 [email protected] AND FK_ItemBetter=i.id AND FK_ItemWorse=i2.id) = 0 
AND (SELECT COUNT(*) FROM Rating WHERE [email protected] AND FK_ItemBetter=i2.id AND FK_ItemWorse=i.id) = 0 
ORDER BY NEWID() 

я не был осведомлен о cross join методом простого перечисления мультипликатор из таблиц раньше.

 Смежные вопросы

  • Нет связанных вопросов^_^