LINQ 中的维恩图样式分组

本文关键字:样式 LINQ | 更新日期: 2023-09-27 18:36:37

Ok.标题可能有点令人困惑,但这是我正在尝试做的

我有一系列自然数

var series = Enumerable.Range(1, 100)

现在我想用GroupBy把数字分成这3组,素数,偶数,奇数

series.Select(number => {
                 var type = "";
                 if (MyStaticMethods.IsPrime(number)) 
                   {
                     Type = "prime";
                   }
                 else if (number % 2 == 0)
                   {
                      type = "Even";
                   }
                 else
                  {
                      type = "Odd";
                  }
                   return new { Type=type, Number = number };
               }).GroupBy(n => n.Type);

现在,上面的查询将错过将偶数或奇数的素数分类为两个类别,它们将只属于"素数"组。上面的选择有什么方法可以产生多个数字吗?

我可以尝试如下方法,但它需要对序列进行额外的扁平化。

series.Select(number => {
                 var list = new List<int>();
                 if (MyStaticMethods.IsPrime(number)) 
                   {
                      list.Add(new { Type="prime", Number = number });
                   }
                 if (number % 2 == 0)
                   {
                      list.Add(new { Type="even", Number = number });
                   }
                 else
                  {
                      list.Add(new { Type="odd", Number = number });
                  }
                   return list;
               })
              .SelectMany(n => n)
            .GroupBy(n => n.Type);   

上面的代码解决了我的问题,有没有更好的方法可以使我的代码看起来更"实用"?

LINQ 中的维恩图样式分组

您可以在此处使用 linq,但您需要复制一些可以存在于不同组中的值。 GroupBy仅适用于不相交的群,因此您需要一种方法来区分偶数22素数。 你所做的方法本质上是你需要做的,但它可以更有效地完成。

您可以定义一组可帮助对数字进行分类的类别。 您不一定需要定义新类才能使其正常工作,但它有助于保持事情的整洁和井井有条。

class Category<T>
{
    public Category(string name, Predicate<T> predicate)
    {
        Name = name;
        Predicate = predicate;
    }
    public string Name { get; }
    public Predicate<T> Predicate { get; }
}

然后,要对数字进行分组,您将执行以下操作:

var series = Enumerable.Range(1, 100);
var categories = new[]
{
    new Category<int>("Prime", i => MyStaticMethods.IsPrime(i)),
    new Category<int>("Odd", i => i % 2 != 0),
    new Category<int>("Even", i => i % 2 == 0),
};
var grouped =
    from i in series
    from c in categories
    where c.Predicate(i)
    group i by c.Name;

这是使用反应式扩展的好情况,因为您将避免重复值。

在下面的代码中,"series"只被解析一次,因为它是一个热源,这要归功于 Publish()。
实际的解析是在"Connect()"期间完成的。

using System.Reactive.Linq;
var list = new List<KeyValuePair<string, int>>();
var series= Observable.Range(1, 100).Publish();
series.Where(e => e % 2 == 0).Subscribe(e=>list.Add(new KeyValuePair<string, int>("Even",e)));
series.Where(e => e % 2 == 1).Subscribe(e => list.Add(new KeyValuePair<string, int>("Odd", e)));
series.Where(e => MyStaticMethods.IsPrime(e) ).Subscribe(e => list.Add(new KeyValuePair<string, int>("Prime", e)));
series.Connect();
var result = list.GroupBy(n => n.Key);