简体   繁体   English

使用linq(C#)比较两个列表的内容,以查看某个特定值是否在某个范围内

[英]Comparing the contents of two lists with linq (C#) to see if one specific value is in a certain range

Ok, I have smashed in my head for the better of the day now. 好吧,我现在已经在我脑海中砸了一天。

I have two lists of custom objects with one property the same at both lists. 我有两个自定义对象列表,两个列表中的一个属性相同。 I need to iterate through both lists and see if the property is the same. 我需要迭代两个列表,看看属性是否相同。

I could do it with nested for-each loops, but I'm rather reluctant if I can do the same with LINQ (and I'm sure I can do that). 我可以用嵌套for-each循环来做,但是如果我能用LINQ做同样的事情我会很不情愿(我确信我能做到这一点)。 I have tried almost everything but I simply can't get to the solution I am looking for. 我已经尝试了几乎所有的东西,但我根本无法找到我正在寻找的解决方案。

Here is the code for the objects I am using for the Lists. 这是我用于列表的对象的代码。

public class Game
{
    // Fields
    private short maxPlayers;
    private Team axis;
    private Team allies;

    // Properties
    public string Name { get; set; }
    public short MaxPlayers
    {
        get
        {
            return maxPlayers;
        }
        set
        {
            if (value > 8)
                maxPlayers = 8;
            else if (value < 2)
                maxPlayers = 2;
            else
                maxPlayers = value;
        }
    }
    public short CurrentPlayers
    {
        get
        {
            int players = axis.Players.Count + allies.Players.Count;
            return (short)players;
        }
    }
    public bool IsFull
    {
        get
        {
            if (CurrentPlayers == MaxPlayers)
                return true;
            else
                return false;
        }
    }
    public Team Axis { get; set; }
    public Team Allies { get; set; }
    public List<Player> Players
    {
        // Somehow this does not work either, so I had to stick with one single team in the for-each loops. Ideas to fix?
        get
        {
            if (allies.Players.Count == 0)
                return axis.Players.Concat(allies.Players).ToList();
            else
                return allies.Players.Concat(axis.Players).ToList();

        }
    }

    //Constructor
    public Game()
    {
        axis = new Team();
        allies = new Team();
    }
}

public class Team
{
    public List<Player> Players { get; set; }

    public EFaction Faction { get; set; }

    public enum EFaction
    {
        Allies,
        Axis,
        Random
    }

    public Team()
    {
        Players = new List<Player>();
        Faction = EFaction.Random;
    }
}

public class Player
{
    private int skillRange = 200;

    public string Name { get; set; }
    public int Skill { get; set; }
    public int SkillRange
    {
        get
        {
            return skillRange;
        }
        set
        {
            if (value >= 200)
                skillRange = value;
            else
                skillRange = 200;
        }
    }
}

At startup I populate the List from a database and do the same with the List. 在启动时,我从数据库填充List并对List执行相同操作。 What I want to do is iterate through the game list and compare the skill property for each player in the game with the skill property of each player on a team. 我想要做的是遍历游戏列表,并将游戏中每个玩家的技能属性与团队中每个玩家的技能属性进行比较。 Here is the for-each loop I used. 这是我使用的for-each循环。 this worked. 这很有效。 But you clearly can see why I want to cut it down so badly. 但是你清楚地看到我为什么要这么糟糕地削减它。

        // Loop through each player in the Automatch queue.
        foreach (Team team in match.TeamsInQueue)
        {
            // Loop through every game in the Atomatch queue.
            foreach (Game game in match.AvailableGames)
            {
                int teamPlayersInSkillRange = 0;

                // Loop through every player in the team and loop through every player in the game.
                foreach (Player teamPlayer in team.Players)
                {
                    int gamePlayersInSkillRange = 0;
                    foreach (Player gamePlayer in game.Allies.Players)
                    {
                        // Compare beoth skill values. If they are in a certain range increase the counter.
                        if (Math.Abs(teamPlayer.Skill - gamePlayer.Skill) <= 200) // The range is currently set for 200, but I want to make it variable later.
                            gamePlayersInSkillRange++;
                    }

                    // Check if the player in the team is in skill range of the game he wants to join. If yes increase the counter.
                    if (gamePlayersInSkillRange == game.Allies.Players.Count)
                        teamPlayersInSkillRange++;
                  }
                // Check if the whole team is in skill range of the game they want to join. If yes return true.
                if (teamPlayersInSkillRange == team.Players.Count)
                {
                    // ToDo: Implement join process here.
                }
            }
        }

Any help would be appreciated. 任何帮助,将不胜感激。 Thanks. 谢谢。

For a given team and game, you want the team to join the game if all of the team's player's are in the skill range of all the game's players. 对于给定的团队和游戏,如果团队的所有玩家都在所有游戏玩家的技能范围内,您希望团队加入游戏。 This sounds like a job for the All() method! 这听起来像是All()方法的工作!

// Loop through each player in the Automatch queue.
foreach (Team team in match.TeamsInQueue)
{
    // Loop through every game in the Atomatch queue.
    foreach (Game game in match.AvailableGames)
    {
        bool allInSkillRange = team.Players.All(t =>
            game.Allies.Players.All(g => Math.Abs(t.Skill - g.Skill) <= 200));
        if(allInSkillRange)
        {
            // ToDo: Implement join process here.
        }
    }
}

If you're interested in an automated way to convert code to LINQ, look at Resharper . 如果您对将代码转换为LINQ的自动方式感兴趣,请查看Resharper

Here's how I came up with the solution. 以下是我提出解决方案的方法。 Using this process along with becoming familiar with LINQ's methods and acquiring experience can make refactoring much easier. 使用此过程以及熟悉LINQ的方法和获取经验可以使重构更容易。 I didn't just look at the code and immediately think of using All() . 我不只是看代码并立即想到使用All() I started by refactoring smaller sections and then going from there. 我开始重构较小的部分,然后从那里开始。 After reading over all of the code I focused on the inner-most loop. 阅读完所有代码后,我专注于最内层的循环。

int gamePlayersInSkillRange = 0;
foreach (Player gamePlayer in game.Allies.Players)
{
    // Compare beoth skill values. If they are in a certain range increase the counter.
    if (Math.Abs(teamPlayer.Skill - gamePlayer.Skill) <= 200) // The range is currently set for 200, but I want to make it variable later.
        gamePlayersInSkillRange++;
}

This counts the number of players that satisfy a condition, which can be refactored into a Count() call: 这会计算满足条件的玩家数量,可以将其重构为Count()调用:

int gamePlayersInSkillRange = game.Allies.Players.Count(g => 
    (Math.Abs(teamPlayer.Skill - g.Skill) <= 200);

The following if statement checks if gamePlayersInSkillRange equals the number of items in game.Allies.Players, which is the list that we were originally counting. 以下if语句检查gamePlayersInSkillRange是否等于game.Allies.Players中的项目数,这是我们最初计算的列表。 Oh, so we're checking if all of those list members satisfy the predicate! 哦,所以我们检查所有列表成员是否满足谓词! We can include that step in the LINQ call by changing Count() to All() . 我们可以通过将Count()更改为All()来在LINQ调用中包含该步骤。 Here is what the next innermost loop looks like now: 这是下一个最内层循环现在的样子:

foreach (Player teamPlayer in team.Players)
{
    bool allGamePlayersInRange = game.Allies.Players.All(g => 
        (Math.Abs(teamPlayer.Skill - g.Skill) <= 200);

    // Check if the player in the team is in skill range of the game he wants to join. If yes increase the counter.
    if (allGamePlayersInRange)
        teamPlayersInSkillRange++;

} }

Now this loop looks like it could be refactored into a Count() call. 现在这个循环看起来可以重构为Count()调用。 But if we examine the code just after it closely, we see it is the exact same pattern as the loop we just refactored, meaning we can jump right into refactoring it into an All() call, completing the refactoring. 但是如果我们仔细检查它之后的代码,我们就会发现它与我们刚刚重构的循环完全相同,这意味着我们可以直接将其重构为All()调用,完成重构。

Try this: 尝试这个:

foreach(Team team in match.TeamsInQueue)
{
    if(team.Players.Insersect(match
           .SelectMany(m => m.AvailableGames, g=> g.Allies.Players), 
            new PlayerSkillComparer().Count() == team.Players.Count()) {
         // ToDO: Implement join process here.
    }
}

Where PlayerSkillComparer implements IEqualityComparer<Player> and its Equals method returns true if the two given Player objects have a skill difference <= 200. See an example here . 其中PlayerSkillComparer实现IEqualityComparer<Player> ,如果两个给定的Player对象具有<= 200的技能差异,则其Equals方法返回true。请参阅此处的示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM