【问题标题】:Comparing the contents of two lists with linq (C#) to see if one specific value is in a certain range使用 linq (C#) 比较两个列表的内容以查看一个特定值是否在某个范围内
【发布时间】:2012-12-09 12:19:45
【问题描述】:

好的,我现在已经把我的脑袋撞坏了。

我有两个自定义对象列表,两个列表中的一个属性相同。我需要遍历这两个列表,看看属性是否相同。

我可以用嵌套的 for-each 循环来做到这一点,但如果我能用 LINQ 做同样的事情,我很不情愿(我确信我能做到)。 我几乎尝试了所有方法,但我根本无法找到我正在寻找的解决方案。

这是我用于列表的对象的代码。

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

在启动时,我从数据库中填充列表并对列表执行相同操作。我想要做的是遍历游戏列表并将游戏中每个玩家的技能属性与团队中每个玩家的技能属性进行比较。这是我使用的 for-each 循环。这行得通。但你清楚地知道我为什么要如此强烈地削减它。

        // 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.
                }
            }
        }

任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 在将这样的东西转换为 LINQ 时,我总是发现从小处着手最容易。首先将最里面的 foreach 循环转换为 LINQ,然后向外移动,直到您对它满意为止。

标签: c# linq loops lambda foreach


【解决方案1】:

对于给定的球队和比赛,如果球队的所有球员都在所有比赛球员的技能范围内,您希望球队加入比赛。这听起来像是 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.
        }
    }
}

如果您对将代码转换为 LINQ 的自动化方式感兴趣,请查看Resharper

这是我想出解决方案的方法。使用此过程并熟悉 LINQ 的方法并获得经验可以使重构变得更加容易。我不只是看代码并立即想到使用All()。我从重构较小的部分开始,然后从那里开始。阅读完所有代码后,我专注于最内层的循环。

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

这计算满足条件的玩家数量,可以重构为Count()调用:

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

下面的 if 语句检查 gamePlayersInSkillRange 是否等于我们最初计算的列表 game.Allies.Players 中的项目数。哦,所以我们正在检查这些列表成员中的 all 是否满足谓词!我们可以通过将Count() 更改为All() 来将该步骤包含在LINQ 调用中。下面是下一个最里面的循环现在的样子:

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

}

现在这个循环看起来可以重构为Count() 调用。但是,如果我们仔细检查它之后的代码,我们会发现它与我们刚刚重构的循环完全相同,这意味着我们可以直接将其重构为 All() 调用,从而完成重构。

【讨论】:

  • 像魅力一样工作。最重要的是,一个精彩的解释如何到达那里。
【解决方案2】:

试试这个:

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

如果两个给定的Player 对象的技能差异PlayerSkillComparer 实现IEqualityComparer&lt;Player&gt; 及其Equals 方法返回true。参见示例here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 2021-01-10
    • 1970-01-01
    相关资源
    最近更新 更多