查找未一起查看的两个元素的组合(LINQ、SQL 或 C#)

发布于 2024-07-14 12:48:36 字数 1288 浏览 11 评论 0原文

我有一个页面显示两个对象,然后用户选择其中之一。 我将偏好和组合记录在 MSSQL 数据库中,并最终存储如下数据:

UserId=1, BetterObjectId=1, WorseObjectId=2

现在我想避免再次显示对象的组合 (1,2 / 2,1)。

那么我如何生成随机组合来向用户显示排除以前查看过的组合?

这似乎应该是一个非常简单的问题,但像大多数程序员一样,我睡眠不足和咖啡,所以非常感谢您的帮助:-)

非常天真的方法是这样的(并且对此函数的所有调用都必须包含在检查中,以查看用户是否已经评级为多次作为 nCr,其中 n 是项目计数,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();
}

编辑

使用一些 SQL,我可以生成所有不完整项目的列表(即,存在涉及以下项目的组合)用户没有见过)但我认为不是特别有用。

我还可以想象在选择 2 个随机项目之前生成所有可能的组合并消除已经查看过的组合,但这是另一个糟糕的解决方案。

一种可能性(对于大 n 来说是内存密集型)是生成所有可能的组合并将combinationId 存储在评级中。 然后,我可以对所有组合进行 SELECT,其中combinationId IS NOT IN (SELECTcombinationId FROM ratings WHERE userId=x),并进行一些更改以反映组合的对称关系。

I have a page that displays two objects and then the user picks one of these. I record the preference and the combination in a MSSQL database and end up storing data like this:

UserId=1, BetterObjectId=1, WorseObjectId=2

Now I would like to avoid showing that combination of objects (1,2 / 2,1) ever again.

So how do I generate random combinations to show the user excluding previously viewed combinations?

This seems like it should be a really straightforward question but like most programmers I'm short on sleep and coffee so your help is much appreciated :-)

The very naive approach is something like this (and all calls to this function would have to be wrapped in a check to see if the user has already rated as many times as nCr where n is the item count and r is 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

Using some SQL I can generate a list of all items that aren't complete (i.e. there is a combination involving the item that the user hasn't seen) but I don't think is particularly useful.

I can also imagine generating every possible combination and eliminating already viewed ones before picking 2 random items but this is a another terrible solution.

A possibility (memory intensive for large n) is to generate all possible combinations and store the combinationId in the rating. Then I can just do a SELECT of all combinations WHERE combinationId IS NOT IN (SELECT combinationId FROM ratings WHERE userId=x) with some changes to reflect the symmetric relationship of combinations.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

远山浅 2024-07-21 12:48:36
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 };

这是一个博客 关于将“随机”翻译到数据库中。

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

If you require that ItemId1 < ItemId2 in the Rating table, you only have to check the Rating table once.

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

Here's a blog about getting "random" translated into the database.

-黛色若梦 2024-07-21 12:48:36

一种解决方案是:

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

我不知道 交叉连接之前只是列出多个FROM表的方法。

One solution is this:

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

I wasn't aware of the cross join method of just listing multiple FROM tables before.

花心好男孩 2024-07-21 12:48:36

假设可用项目列表在数据库中,我将完全在数据库中处理这个问题。 无论如何,您已经在访问数据库了,那么为什么不在那里完成它呢?

Assuming that the list of available items is in the database, I would handle this problem entirely in the database. You are hitting the database already, no matter what, so why not get it done there?

慵挽 2024-07-21 12:48:36

将所有对象放入队列或堆栈中,然后将 2 和 2 弹出,直到它们为空怎么样?

What about putting all the objects in a queue or a stack, and then pop 2 and 2 off until they are empty?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文