使用Linq查找包含一组对象的实例

时间:2010-09-25 01:54:43

标签: c# linq

让我们说我有一套袋子。每个包包含一套大理石。我想选择包含特定大理石组合的袋子。在linq中执行此操作的最有效方法是什么?

在代码中:

public enum Marble { Red, Green, Blue}

public class Bag {
    public string Name;
    public List<Marble> contents;
}
var marbles = new[] { Marble.Red, Marble.Green };
 var bags = new [] 
            {new Bag {Name = "Foo", contents = new List<Marble> {Marble.Blue}},
             new Bag {Name = "Bar", contents = new List<Marble> {Marble.Green, Marble.Red}},
             new Bag {Name = "Baz", contents = new List<Marble> {Marble.Red, Marble.Green, Marble.Blue}}
            };

//Output contains only bag Bar
var output = bags.Where(bag => bag.contents.All(x => marbles.Contains(x)) && 
                               marbles.All(x => bag.contents.Contains(x)));

有更好的方法吗?

6 个答案:

答案 0 :(得分:1)

看来你已经有了一个很好的解决方案。

尝试多种不同的+重复包!您的解决方案足够可读,涵盖了所需的业务逻辑。

 var bags = new[] 
        {new Bag {Name = "Foo", contents = new List<Marble> {Marble.Blue}},
         new Bag {Name = "Bar", contents = new List<Marble> {Marble.Green, Marble.Red}},
         new Bag {Name = "Fiz", contents = new List<Marble> {Marble.Red, Marble.Green}},
         new Bag {Name = "REDS", contents = new List<Marble> {Marble.Red, Marble.Red}},
         new Bag {Name = "Biz", contents = new List<Marble> { Marble.Red } }, 
         new Bag {Name = "Griz", contents = new List<Marble> {Marble.Green, Marble.Green, Marble.Blue}},
         new Bag {Name = "Baz", contents = new List<Marble> {Marble.Red, Marble.Green, Marble.Blue}}
        };

不确定为什么需要Where()中的第二个条件。

这会产生单个匹配项Bar。

 var output2 = bags.Where(b => b.contents.All(x => marbles.Contains(x))
                            && b.contents.Count == marbles.Count());

<击>

答案 1 :(得分:0)

这是另一种解决方案。虽然不确定性能。

var output = bags.Where(bag => !bag.contents.Except(marbles).Any());

不太对。

您可能需要考虑将集合类型从List<Marble>更改为HashSet<Marble>之类的内容。这将允许您这样做:

var output = bags.Where(bag => bag.contents.SetEqual(marbles));

答案 2 :(得分:0)

同意坎贝尔的回答。另一个建议是“为什么不使用[Flags]属性而不是List<MyEnum>”? This question可能会就enum提供一些想法。

答案 3 :(得分:0)

如果你有很多行李并希望获得最佳性能,你可以考虑使用PLinq

public enum Marble { Red, Green, Blue }

public struct Bag
{
    public string Name;
    public List<Marble> contents;
}

class Program
{
    static void Main(string[] args)
    {

        var marbles = new[] { Marble.Red, Marble.Green };
        var bags = new [] 
        {new Bag {Name = "Foo", contents = new List<Marble> {Marble.Blue}},
         new Bag {Name = "Bar", contents = new List<Marble> {Marble.Green, Marble.Red}},
         new Bag {Name = "Baz", contents = new List<Marble> {Marble.Red, Marble.Green, Marble.Blue}}
        };

        //Output contains only bag Bar
        var output = bags.AsParallel<Bag>().Where(bag => bag.contents.All(x => marbles.Contains(x)) &&
                                       marbles.All(x => bag.contents.Contains(x)));

        output.ForAll<Bag>(bag => Console.WriteLine(bag.Name));  // "Bar"
    }
}

答案 4 :(得分:0)

我认为set subraction运算符就是以更清晰的方式完成它的方式。我已经给出了方法调用和linq表达方式。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Project
{
    public enum Marble { Red, Green, Blue}

    public class Bag {
        public string Name;
        public List<Marble> contents;
    }

    class Program
    {
        static void Main()
        {
            var marbles = new[] { Marble.Red, Marble.Green };
            var bags = new[] 
            {
                new Bag {Name = "Foo", contents = new List<Marble> {Marble.Blue}},
                new Bag {Name = "Bar", contents = new List<Marble> {Marble.Green, Marble.Red}},
                new Bag {Name = "Baz", contents = new List<Marble> {Marble.Red, Marble.Green, Marble.Blue}},
                new Bag {Name = "Foo", contents = new List<Marble> {Marble.Blue}},
                new Bag {Name = "Bar", contents = new List<Marble> {Marble.Green, Marble.Red}},
                new Bag {Name = "Fiz", contents = new List<Marble> {Marble.Red, Marble.Green}},
                new Bag {Name = "REDS", contents = new List<Marble> {Marble.Red, Marble.Red}},
                new Bag {Name = "Biz", contents = new List<Marble> { Marble.Red } }, 
                new Bag {Name = "Griz", contents = new List<Marble> {Marble.Green, Marble.Green, Marble.Blue}},
                new Bag {Name = "Baz", contents = new List<Marble> {Marble.Red, Marble.Green, Marble.Blue}}
            };

            // the method call version with set substraction operator
            var query_v2 = bags.Where(bag => bag.contents.Except(marbles).Count() == 0 &&
                                             marbles.Except(bag.contents).Count() == 0                                            
                                     );

            // print out the results 
            Console.WriteLine("query_v2...");
            foreach (var bag in query_v2)
            {
                Console.WriteLine(bag.Name);
            }
            Console.WriteLine();

            // Follwowing is a LINQ Expression version
            var linqversion = from bag in bags
                              let diff1 = bag.contents.Except(marbles).Count()
                              let diff2 = marbles.Except(bag.contents).Count()
                              where diff1 == 0 && diff2 == 0 // perfect match ?
                              select bag;

            Console.WriteLine("Linq expression version output...");
            foreach (var bag in linqversion)
            {
                Console.WriteLine(bag.Name);
            }
            Console.ReadLine();
        }
    }
}

答案 5 :(得分:0)

我认为set subraction运算符就是以更清晰的方式完成它的方式。我已经给出了方法调用和linq表达方式。

    using System;
    using System.Collections.Generic;
    using System.Linq;


    namespace Project
    {
            public enum Marble { Red, Green, Blue}

            public class Bag {
                public string Name;
                public List<Marble> contents;
            }




    class Program
    {

        static void Main()
        {

            var marbles = new[] { Marble.Red, Marble.Green };
            var bags = new[] 
                    {new Bag {Name = "Foo", contents = new List<Marble> {Marble.Blue}},
                     new Bag {Name = "Bar", contents = new List<Marble> {Marble.Green, Marble.Red}},
                     new Bag {Name = "Baz", contents = new List<Marble> {Marble.Red, Marble.Green, Marble.Blue}},
                     new Bag {Name = "Foo", contents = new List<Marble> {Marble.Blue}},
                     new Bag {Name = "Bar", contents = new List<Marble> {Marble.Green, Marble.Red}},
                     new Bag {Name = "Fiz", contents = new List<Marble> {Marble.Red, Marble.Green}},
                     new Bag {Name = "REDS", contents = new List<Marble> {Marble.Red, Marble.Red}},
                     new Bag {Name = "Biz", contents = new List<Marble> { Marble.Red } }, 
                     new Bag {Name = "Griz", contents = new List<Marble> {Marble.Green, Marble.Green, Marble.Blue}},
                     new Bag {Name = "Baz", contents = new List<Marble> {Marble.Red, Marble.Green, Marble.Blue}}
                    };





          // the method call version with set substraction   operator

           var  query_v2      = bags.Where(bag => bag.contents.Except(marbles).Count() == 0 &&
                                           marbles.Except(bag.contents).Count() == 0                                            
                                    );



               // print out the results 

               Console.WriteLine("query_v2...");

               foreach (var bag in query_v2)
               {
                   Console.WriteLine(bag.Name);
               }



           Console.WriteLine();








           // Follwowing is a LINQ Expression version
           //
           var linqversion = from bag in bags
                             let diff1 = bag.contents.Except(marbles).Count()
                             let diff2 = marbles.Except(bag.contents).Count()
                             where diff1 == 0 && diff2 == 0 // perfect match ?
                             select bag;


           Console.WriteLine("Linq expression version output...");

            foreach (var bag in linqversion)
            {
                Console.WriteLine(bag.Name);
            }


            Console.ReadLine();
        }









    }

}