【问题标题】:Turning a chain of foreach's for inserting elements from a list into another list into linq将用于将列表中的元素插入另一个列表的 foreach 链转换为 linq
【发布时间】:2013-08-22 19:42:57
【问题描述】:

我已经研究了几个小时,我得到的唯一结论是我需要拿起一本关于 Linq 的好书。

所以这是交易:我有以下类的三个对象列表(从现在开始我将它们称为“父列表”:

    public class State
    {
        public int IdState {get; set;}
        public string NameState {get; set;}
        private IList<City> cityList = new List<City>();

        public void AddCity(City city)
        {
            cityList.Add(city);
        }
    }

    public class City
    {
        public int IdCity { get; set; }
        public string NameCity { get; set; }
        public int IdState { get; set; }
        private IList<Company> companyList = new List<Company>();

        public void AddCompany(Company company)
        {
            companyList.Add(company);
        }
    }

    public class Company
    {
        public int IdCompany { get; set; }
        public string NameCompany { get; set; }
        public int IdCity { get; set; }
    }

我认为从这里可以很直接地解释我想要什么:一个州列表,其中 cityList 填充了该州的适当城市,每个城市中的每个 companyList 列表都填充了该城市的公司.换句话说,一个州列表,每个州分支到其城市,然后每个城市分支到其中的公司。

所以,我的父母名单是:

    private List<State> finalList; //This is the "parent list supreme" which I'll send to the client-side
    private List<City> cityList; //Auxiliary
    private List<Company> companyList; //Auxiliary; this one does not exist in the actual code but I'm putting it here for simplification purposes

真的没关系,但多亏了 linq“魔法”,我能够用正确的州、城市和公司填写这些列表,但是,我不知道如何填写正确的城市和公司通过链接进入 State.cityList 和 City.companyList。就目前而言,我正在使用一个非常丑陋的 foreach 链:

        foreach (State state in finalList)
        {
            foreach (City city in cityList)
            {
                if (city.IdState == state.IdState)
                {
                    state.AddCity(city);

                    foreach (Company company in companyList)
                    {
                        if (company.idCity == city.IdCity)
                            city.AddCompany(company);
                    }
                }
            }
        }

丑陋,对吧?那么,我该如何用 linq 实现同样的目标呢?我认为可能是一个更有效的问题:在这种情况下是否值得使用 linq(它都指向“是”但数字......)?

顺便说一句:这种方式正如我所期望的那样工作

【问题讨论】:

  • 我不明白你想要达到什么目的。 finalList 是否预先填充了州,而您只是想将城市和公司插入到孩子中?还是倒过来了?
  • 在您的示例代码中,这部分令人困惑:if (line.idCity == city.IdCity) 因为它在父 foreach 的上下文中似乎没有意义。在 foreach 中既没有更改 line 也没有更改 city,因此如果这是正确的,是否应该将其移到 foreach 之外,因为它对于 companyList 的所有迭代总是为真或总是为假?
  • @RobertMcKee 感谢您指出这一点,这是一个“剩余代码”,因为在实际代码中我不使用公司的支持列表。已编辑以符合要求。

标签: c# linq list foreach


【解决方案1】:

您在逻辑上在这里做的是一个 Join 操作。您当前的代码实际上是查找所有可能的城市和州对,然后仅归档匹配的对。您可以在 LINQ 中表示这一点,但您不想这样做。有更有效的方法来执行只生成您需要开始的确切对的连接。 LINQ Join 运算符利用了这一点。

您的代码归结为两个不同的Join 调用,然后对每个结果进行迭代,根据查询执行对象的变异。

var stateCityPairs = from state in finalList
                        join city in cityList
                        on state.IdState equals city.IdState
                        select new { state, city };

foreach (var pair in stateCityPairs)
    pair.state.AddCity(pair.city);


var cityCompanyPairs = from city in cityList
                        join company in companyList
                        on city.IdCity equals company.IdCity
                        select new { city, company };

foreach (var pair in cityCompanyPairs)
    pair.city.AddCompany(pair.company);

虽然这实际上是多一点的代码,但对于更大的集合,这会表现得更好,并且在语义上也代表了代码的意图。

【讨论】:

  • 谢谢Servy,我回家后会尽快告诉你
  • 我已经在我的虚拟应用程序中尝试过这段代码,它运行良好。
  • @It'sNotALie。自然。
  • @Servy 正如预期的那样,将代码放入实际项目需要一些微调(事实上,我根本不需要更改您的代码)。如果可以的话,我会吻你。做得好。还有关于 foreach 与 ForEach 的不错的提示。再一次,非常感谢,我想是时候阅读一些 Linq 书籍了。有什么建议吗?
  • @S.O.并不真地。 Jon Skeet's blog series 重新实现它非常有趣,当然他还有许多其他关于 LINQ 和其他有趣主题的帖子,但其中一些更高级。
【解决方案2】:

我在 City 和 State 类中添加了 2 个方法。如果列表是公开的,那就更容易了:

public void AddCities(List<City> cities)
{
    cityList.Concat(cities);
}

public void AddCompanies(List<Company> companies)
{
    companyList.Concat(companies);
}

您可以使用所需的聚合器。我通常更喜欢 Union,因为它不会添加双打,但我想这对你来说并不重要。

她是我在 Linq 中的做法:

cityList.ForEach(ci => ci.AddCompanies(companyList.Where(co => co.IdCity == ci.IdCity).ToList()));
finalList.ForEach(s => s.AddCities(cityList.Where(ci => ci.IdState == s.IdState).ToList()));

我首先将公司添加到正确的城市,然后添加正确状态的所有城市。 如果您需要有关此代码的更多说明,请随时询问。

【讨论】:

  • 1) 您在 AddCities 中使用的 Concat 不正确。 Concat 创建一个新序列,您只需将其放在地板上即可。而是接受IEnumerable 作为输入(也可以允许延迟执行)并在方法内部调用AddRange。 2) ForEach 并不是真正的 LINQ,并且不比仅使用 foreach 循环更好。 3)你正在做OP正在做的事情并找到所有对然后过滤。这Join效率低很多
  • @Servy AddRange 位于 List&lt;T&gt;,而不是 IList&lt;T&gt;,因此此处不可用。这不会改变您任何积分的有效性。
  • @cadrell0 确实如此。这就是为什么我不喜欢人们存储IList等字段的原因;我更愿意将其存储为List。感谢您指出这一点;我确实错过了。
  • @Servy 实际上,在将它们声明为 IList 之前给我带来了很多麻烦,因为它们只是不支持 linq 扩展方法,因此我最终对它们都使用了 List。顺便说一句,我将在实际解决方案中测试您的代码。手指交叉!
【解决方案3】:

使用 LINQ,您可能希望将私有 IList 转换为公共 IList(或 List 或 IEnumerable 或 IQueryable),以便直接设置它们。

假设 finalList 已经预先填充了州:

var finalList=finalList
    .Select(fl=>new State
    {
        fl.IdState,
        fl.NameState,
        cityList=cityList.Where(cl=>cl.IdState==fl.IdState).Select(cl=>
            new City
            {
                cl.IdCity,
                cl.NameCity,
                cl.IdState,
                companyList=companyList.Where(comp=>
                    comp.IdState==cl.IdState && comp.IdCity==cl.IdCity)
            }
    });

【讨论】:

  • 根本不需要。您只需执行查询以查看添加的内容,然后通过foreach 将它们全部添加,正如我在回答中所示。虽然您当然可以提供一次添加多个值的方法,但这当然不是必需
  • 我没有说这是必需的,只是他愿意。他说他想用 LINQ 来做,而 forEach 不是 LINQ,它是 IEnumerable 的一个方法。
  • 那么这不是问题的答案,充其量只是评论。 foreach 不是任何方法,它是语言关键字。你是对的,它不是 LINQ。除了 LINQ 查询之外,您还可以使用它,就像一般大部分 LINQ 查询一样。
  • ForEach 因为你使用它不是关键字,它是 List 的方法(对不起,我以为它是 IEnumerable,但它实际上是 List)。 msdn.microsoft.com/en-us/library/bwabdf9z.aspxforeach 因为他使用它是一个关键字。
  • 我根本没有使用 List 的 ForEach 方法。我刚刚使用了foreach 关键字。我从不使用ForEach。我会让 Eric Lippert explain why.
猜你喜欢
  • 1970-01-01
  • 2020-12-06
  • 2022-11-18
  • 2021-07-16
  • 2013-02-17
  • 1970-01-01
  • 2022-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多