【问题标题】:Using Linq to find instances that contain a set of objects使用 Linq 查找包含一组对象的实例
【发布时间】:2010-09-30 04:34:02
【问题描述】:

假设我有一套包。每个袋子都包含一组弹珠。我想选择包含特定弹珠组合的袋子。在 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)));

有没有更好的办法?

【问题讨论】:

    标签: c# linq


    【解决方案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());
    

    【讨论】:

    • 我一直在考虑这个问题。至于为什么需要第二个 where ,这里有一个反例。 new Bag { Name = "Biz", contents = new List&lt;Marble&gt; { Marble.Red } }。据我所知,他正在寻找集合等价。
    • @Jeff 确实如此。我的解决方案也未能计数,只有 2 个红色。 :O
    【解决方案2】:

    我认为 set 减法运算符是按照它应该以更简洁的方式完成的操作。我已经给出了方法调用和 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();
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      同意坎贝尔的回答。另一个建议是“为什么不使用[Flags] 属性而不是List&lt;MyEnum&gt;”? This question 可能会给你一些关于enum 的想法。

      【讨论】:

        【解决方案4】:

        如果您有很多包并且想要获得最佳性能,您可以考虑使用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"
            }
        }
        

        【讨论】:

          【解决方案5】:

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

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

          不太对。

          您可能需要考虑将集合类型从List&lt;Marble&gt; 更改为HashSet&lt;Marble&gt;。这将允许您这样做:

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

          【讨论】:

            【解决方案6】:

            我认为 set 减法运算符是按照它应该以更简洁的方式完成的操作。我已经给出了方法调用和 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();
                    }
            
            
            
            
            
            
            
            
            
                }
            
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多