Linq获取不同值

3

我有一个查询:

var q = (
    from c in db.tblStoreRecommendations
    where
    itemIDsInCart.Contains(c.ItemID)
    && !itemIDsInCart.Contains(c.RecommendItemID)
    select c
);

它将返回类似以下内容的结果:
ID    ItemID    RecommendItemID    Message
------------------------------------------
1     25        3                  Msg here
2     26        3                  Something else
3     27        8                  Another message

我需要一个查询来筛选出具有相同RecommendItemID的结果,这些结果不应该在返回的结果中出现超过一次。

如果存在两个相同的结果,可以使用任何一个(随机选择最好)。因此,返回的结果应省略记录ID 1或2。

请问是否有人能向我展示如何做到这一点?谢谢!

3个回答

6
一种方法是使用GroupBy,然后从每个组中选择第一个项:
var q = (
    from c in db.tblStoreRecommendations
    where
        itemIDsInCart.Contains(c.ItemID)
     && !itemIDsInCart.Contains(c.RecommendItemID)
    select c
).GroupBy(c => c.RecommendItemID)
 .Select(g => g.First());

如果您要使用此功能来显示随机评论,则建议将其插入使用代码而不是LINQ查询中,方法是省略First,如下所示:
var q = (
    from c in db.tblStoreRecommendations
    where
        itemIDsInCart.Contains(c.ItemID)
     && !itemIDsInCart.Contains(c.RecommendItemID)
    select c
).GroupBy(c => c.RecommendItemID)
 .Select(g => g.ToArray());

var random = new Random();
foreach (var r in q)
{
    var rec = r[random.Next(r.Length)];
    // use your recommendation
}

非常感谢!不过有一个问题,它没有随机返回重复项。对此有什么想法吗? - Tom Gullen
@TomGullen:我提供了另一种方法(使用较少的LINQ进行随机选择)。 - user7116
回答不错。但是有没有人能够解释一下为什么需要 var rec = r.Length == 1 ? r[0] : r[random.Next(r.Length)];random.Next 返回的数字已经在零(包括)和长度(不包括)之间了... - Adam

3
我相信您可以在Linq表达式的末尾添加一个独特的比较,例如:
var q = (
  from c in db.tblStoreRecommendations
  where
  itemIDsInCart.Contains(c.ItemID)
  && !itemIDsInCart.Contains(c.RecommendItemID)
  select c
)
.Distinct((x, y) => x.RecommendItemID == y.RecommendItemID);

编辑

我刚意识到我是使用我自己写的扩展方法完成了这个操作,通常我都会保留该方法... 下面是代码:

    /// <summary>
    /// Provides a way to get a distinct list of items based on a lambda comparison operator.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <param name="equalityComparer">A comparison function that returns true of the two items are considered equal.</param>
    /// <returns>The list of distinct items.</returns>
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> equalityComparer)
    {
        var distincts = new List<T>();
        foreach (var item in source)
        {
            var found = false;
            foreach (var d in distincts)
            {
                found = equalityComparer(item, d);
                if (found)
                    break;
            }

            if (!found)
            {
                distincts.Add(item);
                yield return item;
            }
        }
    }

1
请注意,您需要创建一个实现了 IEqualityComparer<tblStoreRecommendation> 接口的类,而不仅仅是直接使用 Lambda 表达式。请参阅 http://msdn.microsoft.com/en-us/library/bb338049.aspx。 - Tim S.
1
我认为Distinct重载需要一个实现IEqualityComparer<TSource>接口的类型,而不是一个谓词。 - user7116
啊,你说得对。我是在VisualStudio中的一个现有项目中输入了这个代码,我碰巧有自己的扩展方法来完成它...请看我的编辑。 - CodingWithSpike

1

让我们尽可能地坚持使用查询表达式:

var q = from c in db.tblStoreRecommendations
        where itemIDsInCart.Contains(c.ItemID)
          && !itemIDsInCart.Contains(c.RecommendItemID)
        group c by c.RecommendItemID into matches
        select matches.First();

在这里,上下文关键字 into 确实非常方便。

以下是如何随机化它:

private static Random random = new Random();

var q = from c in db.tblStoreRecommendations
        where itemIDsInCart.Contains(c.ItemID)
          && !itemIDsInCart.Contains(c.RecommendItemID)
        group c by c.RecommendItemID
        into matches
        select matches.AsEnumerable().ElementAt(random.Next(matches.Count()));

由于LINQ to SQL无法处理ElementAt调用,因此我编辑了答案,在选择分组中的随机元素之前执行查询(通过调用AsEnumerable)。


+1,虽然我不确定L2S或L2E如何处理ElementAt / Count组合。您可能需要在两者之间使用AsEnumerable调用来实例化结果。 - user7116
好的观点,我在家没有 SQL Server... 有人可以确认一下吗? - Adam
谢谢您的回复,但是我遇到了错误 System.NotSupportedException: The query operator 'ElementAt' is not supported. - Tom Gullen
谢谢你提醒我,你能试试修改后的代码吗?那应该已经解决了。感谢@sixlettervariables的建议。有没有什么简单的方法可以在不运行SQL Server的情况下测试这样的代码呢? - Adam
@codesparkle:我不知道除了阅读晦涩的MSDN文档之外还有什么其他方法可以确定哪些是可行的,哪些是不可行的。(顺便提一下,在AsEnumerable之前需要将整个查询作为括号内的内容。) - user7116

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接