如何使用LINQ或Lambda而不是嵌套和多个foreach语句

时间:2015-02-10 15:20:45

标签: c# linq loops lambda

如何使用LINQ或Lambda代替嵌套和多个foreach语句。

我想使用比嵌套的foreach语句更好的语法来覆盖第二个列表中的项目的初始列表。

在下面的代码中:

  • 我想用secondList中具有相同值的那些覆盖initialList。 (删除红色)
  • 使用secondList中的值,其中Value相同(黄色) 新的初始列表列表应包括(绿色和黄色)

    static void Main(string[] args)
    {
        int useProd = 2;
        int useDomain = 0;
    
        var person1 = new Person() { prodId = 1, Value = "foo", domainId = 0, Name = "Red" };
        var person2 = new Person() { prodId = 1, Value = "bar", domainId = 0, Name = "Green" };
        var person3 = new Person() { prodId = 1, Value = "foo", domainId = 1, Name = "Yellow" };
    
        var initialList = new List<Person>();
        initialList.Add(person1);
        initialList.Add(person2);
    
        var secondList = new List<Person>();
        secondList.Add(person3);
    
        List<Person> personsToRemove = new List<Person>();
        List<Person> personsToUpdate = new List<Person>();
    
        foreach (var pers1 in initialList)
        {
            foreach (var pers2 in secondList)
            {
                if (pers1.Value == pers2.Value)
                {
                    personsToRemove.Add(pers1);
                    personsToUpdate.Add(pers2);
                }
            }
        }
        foreach (var remPers in personsToRemove)
        {
            initialList.Remove(remPers);
        }
        foreach (var updPers in personsToUpdate)
        {
            initialList.Add(updPers);
        }
        foreach (var item in initialList)
        {
            Console.WriteLine(String.Format("Value: {0}, prodId: {1}, domainId: {2}, Name: {3}", item.Value, item.prodId, item.domainId, item.Name));
        }
    
        Console.ReadKey();
    }
    public class Person
    {
        public int prodId { get; set; }
        public string Value { get; set; }
        public int domainId { get; set; }
        public string Name { get; set; }
    }
    

2 个答案:

答案 0 :(得分:1)

您的嵌套循环最有效地用join表示。此外,在整个列表中不必进行线性搜索只是为了删除一个元素然后添加一个新元素,这将有助于提高效率。我们可以使用Enumerable.Select()的重载将项索引嵌入到结果中,以便可以直接替换元素。

总而言之,它看起来像这样:

var join = from p1 in initialList.Select((p, i) => new { Person = p, Index = i })
           join p2 in secondList on p1.Person.Value equals p2.Value
           select new { Index = p1.Index, Replacement = p2 };

foreach (var item in join.ToList())
{
    initialList[item.Index] = item.Replacement;
}

上面的代码替换了原始代码,从personsToRemovepersonsToUpdate列表的声明开始,以及前三个foreach循环(即除了显示最终结果的所有循环外) )。

注意:

  • 代码从initialList合成一个包含Person实例的匿名类型以及列表中该实例的索引。
  • join子句将Value属性相等的每个列表中的所有项配对。

重要提示:如果任一列表中有多个元素具有相同的Value属性,则每个元素都与另一个列表中具有相同Value的所有其他元素配对。即如果initialList有两个元素Value为&#34; foo&#34;并且secondList有三个这样的元素,您将在结果连接中结束六个元素。你的问题没有定义这是否可能,也不是你想要发生什么,所以我在这里忽略了这种可能性。 :)

  • join结果投射到一个新的匿名类型,其中包含要替换的元素的索引和新值。
  • 通过调用ToList()来实现查询结果。这是必要的,因为否则延迟了连接,修改initialList会使查询无效。
  • 当然,在剩余的foreach中,代码需要做的就是为列表中的相应索引位置分配查询确定的替换值。

答案 1 :(得分:1)

您也可以使用泛型。以下是适用于您的短代码:

    initialList.ForEach(p =>
    {
        if (secondList.Any(sp => sp.Value == p.Value))
        {
            initialList.Remove(p);
            initialList.Add(secondList.Single(spu => spu.Value == p.Value));
        };
    });