【问题标题】:How many times people Share the same items C#人们多少次共享相同的项目C#
【发布时间】:2018-01-26 18:42:40
【问题描述】:

我有一群朋友,他们有 0 、 1 或许多与之相关的令牌。 我试图找出朋友分享同一个令牌的次数。所以如果friend1有令牌1、2和3,friend2有令牌1和2,朋友3有令牌1,2和3。它将返回朋友2和friend1共享2个令牌,而friend3 和friend1分享3个代币等。 我有对令牌进行分组并获取与它们相关联的朋友数量的代码。我只是不确定如何与朋友共享令牌。我在想可能有一些方法可以用 linq 做到这一点,但不确定如何或是否有另一种更好的方法。

var summaryOfFriendsAndTokens = distinctFriendList.Where(x => x.oToken != null) // Check for null in the model
    .SelectMany(x => x.oToken) // Flatten
    .GroupBy(x => x.TokenId) // Group

    .Select(group => new // Project
        {
            ItemName = group.Key,
            TotalQuantity = group.Count()
        })
        .ToList();
        ;
    }
}

public class Token {
    public int TokenId { get; set; }
}

public class Friend {
    public List<Token> otoken = new List<Token>();
    private int friendId = -1;
    public int FriendID {
        get { return friendId; }
        set { friendId = value; }
    }

    public List<Token> oToken {
        get { return otoken; }
        set { otoken = value; }
    }

}

【问题讨论】:

  • 不会是List&lt;Token&gt; sharedTokens = friend1.oToken.Intersect(friend2.oToken).ToList();之类的吗?

标签: c# list linq group-by


【解决方案1】:

你提到...

或者如果有其他更好的方法。

这有帮助吗?

void Main()
{
    var t1 = new Token {TokenId = 1};
    var t2 = new Token {TokenId = 2};
    var t3 = new Token {TokenId = 3};

    var p1 = new Friend {FriendID = 1, oToken = new List<Token>{t1,t2,t3}};
    var p2 = new Friend {FriendID = 2, oToken = new List<Token>{t1,t2}};
    var p3 = new Friend {FriendID = 3, oToken = new List<Token>{t1,t2,t3}};

    var friends = new List<Friend>{p1,p2,p3};

    for (int i = 0; i < friends.Count; i++)
    {
        for (int j = i+1; j < friends.Count; j++)
        {
            Console.WriteLine("Friend {0} and Friend {1} share {2} tokens", friends[i].FriendID, friends[j].FriendID, friends[i].oToken.Intersect(friends[j].oToken).Count());
        }
    }
}

使用与您定义的相同的类。

【讨论】:

  • 我发现它适用于您的代码我将不得不检查我的代码是否存在其他问题。
  • 要使用 linq 完成此操作,您可能需要使用复杂的查询,这种方法比等效的 linq 解决方案更易于理解且更高效。
【解决方案2】:

如果只是比较两个朋友,我们可以做一些简单的事情,比如:friend1.Tokens.Intersect(friend2.Tokens)

对于朋友列表,我们可能希望将相关数据收集到列表中。对于列表中的每个朋友,我们可以通过Where 子句中使用上面的代码来获取与他共享令牌的其他朋友。

我们还可以通过在朋友组的Tokens 属性上使用SelectMany 来获取共享令牌列表。通过这两个列表,我们可以轻松地显示每个朋友和所有其他人之间的共享数据。

注意:我修改了类以覆盖ToString() 方法以简化将它们写入控制台:

public class Token
{
    public int Id { get; set; }

    public override string ToString()
    {
        return $"token{Id}";
    }
}

public class Friend
{
    public int Id { get; set; }
    public List<Token> Tokens { get; set; }

    public override string ToString()
    {
        return $"friend{Id}";
    }
}

这是显示一组朋友之间共享令牌信息的一个示例。下面的代码创建了三个朋友的列表,其中第一个和第三个具有标记 1、2 和 3,第二个具有标记 1 和 2(注意我在 @987654330 的右侧调用了 .ToList() @赋值以确保这些列表在内存中都有单独的位置):

private static void Main()
{
    var tokens = new List<Token>
    {
        new Token {Id = 1},
        new Token {Id = 2},
        new Token {Id = 3},
    };

    var friends = new List<Friend>
    {
        new Friend {Id = 1, Tokens = tokens.ToList()},
        new Friend {Id = 2, Tokens = tokens.Take(2).ToList()},
        new Friend {Id = 3, Tokens = tokens.ToList()},
    };

    // List out our friends and their shared tokens
    foreach (var friend in friends)
    {
        // Get the friends where the intersection of this friend's tokens 
        // and the other friend's tokens has one or more items
        var sharedFriends = friends.Where(f =>
            f.Id != friend.Id &&
            f.Tokens.Intersect(friend.Tokens).Any());

        if (sharedFriends.Any())
        {
            var sharedTokens = friend.Tokens.Intersect(
                sharedFriends.SelectMany(f => f.Tokens));

            Console.WriteLine($"\n{friend} shares {sharedTokens.Count()} tokens:");

            foreach (var sharedToken in sharedTokens)
            {
                var friendsSharingThisToken =
                    sharedFriends.Where(f => f.Tokens.Contains(sharedToken));

                Console.WriteLine(" - {0} shared with: {1}", sharedToken, 
                    string.Join(", ", friendsSharingThisToken));
            }
        }
        else
        {
            Console.WriteLine($"\n{friend} is not sharing tokens with anyone else.");
        }
    }

    Console.Write("\nDone!\nPress any key to exit...");
    Console.ReadKey();
}

输出

【讨论】:

    【解决方案3】:

    使用提供可能的朋友组合的扩展方法:

    public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k) {
        return k == 0 ? new[] { new T[0] } :
          elements.SelectMany((e, i) =>
            elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
    }
    

    您可以计算每对朋友共享了多少代币,并丢弃不共享代币的对。 LINQ Intersect 方法 produces the set intersection of two sequences by using the default equality comparer to compare values 然后我们计算那些常见的元素。

    var tokensFriendCount = distinctFriendList.Where(f => f.oToken != null)
                                              .Combinations(2) // get pairs of friends
                                              .Select(fp => fp.ToList()) // convert each sub-IEnumerable to List to make below simpler
                                              .Select(fp => new {
                                                  Friend1 = fp[0],
                                                  Friend2 = fp[1],
                                                  SharedCount = fp[0].oToken.Intersect(fp[1].oToken).Count()
                                              })
                                              .Where(fpc => fpc.SharedCount > 0); // throw out pairs that share no tokens
    

    上面的计数假设每个Token 对象是唯一的,特别是TokenId 是唯一的,如果这不是真的(尽管您之前的查询暗示它是),您可以根据TokenId 进行计数:

    SharedCount = fp[0].oToken.Select(t => t.TokenId).Intersect(fp[1].oToken.Select(t => t.TokenId)).Count()
    

    如果你只想要一个摘要,你可以简化答案:

    var summarySharedTokenCount = tokensFriendCount.Select(tfc => new {
        Friend1Id = tfc.Friend1.FriendID,
        Friend2Id = tfc.Friend2.FriendID,
        tfc.SharedCount
    }).ToList();
    

    如果不想使用扩展,可以直接生成这些组合:

    var tokensFriendCount = distinctFriendList.Where(f => f.oToken != null)
                                              .SelectMany(f => distinctFriendList.Where(f2 => f2.oToken != null && f2.FriendID > f.FriendID).Select(f2 => new[] { f, f2 }))
                                              .Select(fp => new {
                                                  Friend1 = fp[0],
                                                  Friend2 = fp[1],
                                                  SharedCount = fp[0].oToken.Select(t => t.TokenId).Intersect(fp[1].oToken.Select(t => t.TokenId)).Count()
                                              })
                                              .Where(fpc => fpc.SharedCount > 0);
    

    【讨论】:

    • @NetMange“组合”未被识别。我需要参考什么吗?
    • 您需要在项目中的公共静态类中添加扩展方法(第一块代码)。
    • @AmetureBoss 我还添加了一个不使用扩展方法的替代方案。
    • 它对交叉部分如何工作的简要说明会很好。
    • Intersect 上添加了指向 Microsoft 文档的链接。
    猜你喜欢
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-14
    • 1970-01-01
    相关资源
    最近更新 更多