我想对包含整数List<int>
的列表进行分组。
List<CNode> cNodes
并且CNode是
public class CNode
{
public List<int> Elements;
// ...
}
我可以像这样将cNodes
分组
var groups = cNodes.GroupBy(node => node.Elements[0]);
foreach (var group in groups )
{
// ...
}
但是您看到分组取决于第一个元素,我想按所有元素分组
例如,如果node.Elements.Count == 5
的预期分组结果应与以下内容相同:
var groups = cNodes.GroupBy(node => new
{
A = node.Elements[0],
B = node.Elements[1],
C = node.Elements[2],
D = node.Elements[3],
E = node.Elements[4]
});
我找不到解决方案。
谢谢。
答案 0 :(得分:1)
您可以将node.Take(5)
之类的内容与适当的IEqualityComparer
一起使用,如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var cNodes = new List<CNode>
{
new CNode{Elements = new List<int>{ 0, 0, 1, 1, 1 } },
new CNode{Elements = new List<int>{ 0, 0, 0, 1, 1 } },
new CNode{Elements = new List<int>{ 0, 1, 1, 0 } },
new CNode{Elements = new List<int>{ 0, 1, 1, 0, 0 } },
new CNode{Elements = new List<int>{ 0, 0, 0, 0 } },
new CNode{Elements = new List<int>{ 0, 0, 0, 0 } }
};
Console.WriteLine("\tGroup by 2:");
foreach (var group in cNodes.GroupByElements(2))
Console.WriteLine($"{string.Join("\n", group)}\n");
Console.WriteLine("\tGroup by 3:");
foreach (var group in cNodes.GroupByElements(3))
Console.WriteLine($"{string.Join("\n", group)}\n");
Console.WriteLine("\tGroup by all:");
foreach (var group in cNodes.GroupByElements())
Console.WriteLine($"{string.Join("\n", group)}\n");
}
}
static class CNodeExtensions
{
public static IEnumerable<IGrouping<IEnumerable<int>, CNode>> GroupByElements(this IEnumerable<CNode> nodes) =>
nodes.GroupByElements(nodes.Min(node => node.Elements.Count));
public static IEnumerable<IGrouping<IEnumerable<int>, CNode>> GroupByElements(this IEnumerable<CNode> nodes, int count) =>
nodes.GroupBy(node => node.Elements.Take(count), new SequenceCompare());
private class SequenceCompare : IEqualityComparer<IEnumerable<int>>
{
public bool Equals(IEnumerable<int> x, IEnumerable<int> y) => x.SequenceEqual(y);
public int GetHashCode(IEnumerable<int> obj)
{
unchecked
{
var hash = 17;
foreach (var i in obj)
hash = hash * 23 + i.GetHashCode();
return hash;
}
}
}
}
internal class CNode
{
public List<int> Elements;
public override string ToString() => string.Join(", ", Elements);
}
}
输出为:
Group by 2:
0, 0, 1, 1, 1
0, 0, 0, 1, 1
0, 0, 0, 0
0, 0, 0, 0
0, 1, 1, 0
0, 1, 1, 0, 0
Group by 3:
0, 0, 1, 1, 1
0, 0, 0, 1, 1
0, 0, 0, 0
0, 0, 0, 0
0, 1, 1, 0
0, 1, 1, 0, 0
Group by all:
0, 0, 1, 1, 1
0, 0, 0, 1, 1
0, 1, 1, 0
0, 1, 1, 0, 0
0, 0, 0, 0
0, 0, 0, 0
答案 1 :(得分:1)
您写道:
我想按所有元素分组
Alex提供的解决方案将仅按有限数量的元素分组。您说过,即使您有一个包含100个元素的CNode,也希望按所有元素对其进行分组。此外:如果其中一个CNode的属性Elements等于null,他的解决方案也会崩溃。
因此,让我们创建一个满足您要求的解决方案。
返回值将是一组序列,其中每个组都有一个Key,它是CNode的序列。该组中的所有元素都是具有属性Elements等于Key的所有源CNode。
等于,表示SequenceEqual。因此Elements [0] ==键[0]和Elements [1] ==键[1],等等。
当然,您想确定Elements [0]何时等于Key [0]:是否要按引用进行比较(同一对象)?还是两个具有相同属性值的CNode相等?还是要指定IEqualityComparer<CNode>
,以便如果它们具有相同的名称或ID,就可以看到它们相等?
// overload without IEqualityComparer, calls the overload with IEqualityComparer:
IEnumerable<IGrouping<IEnumerable<Cnode>, CNode>> GroupBy(
this IEnumerable<CNode> cNodes)
{
return GroupBy(cNodes, null);
}
// overload with IEqualityComparer; use default CNode comparer if paramer equals null
IEnumerable<IGrouping<IEnumerable<Cnode>, CNode>> GroupBy(
this IEnumerable<CNode> cNodes,
IEqualityComparer<CNode> cNodeComparer)
{
// TODO: check cNodes != null
if (cNodeComparer == null) cNodeComparer = EqualityComparer<CNode>.Default;
CNodeSequenceComparer nodeSequenceComparer = new CNodeSequenceComparer()
{
CNodeComparer = cNodeComparer,
}
return sequenceComparer.GroupBy(nodeSequenceComparer);
}
您已经注意到我已经将问题转移到新的EqualityComparer上:此比较使用两个CNode序列,并使用提供的SequenceEqual
声明两个IEqualityComparer<CNode>
是否相等:
class CNodeSequenceComparer : IEqualityComparer<IEnumerable<CNode>>
{
public IEqualityComparer<CNode> CNodeComparer {get; set;}
public bool Equals(IEnumerable<CNode> x, IEnumerable<CNode> y)
{
// returns true if same sequence, using CNodeComparer
// TODO: implement
}
}
我们要记住的一件事是,您的属性Elements的值可能为null(毕竟,您没有指定不是这种情况)
public bool Equals(IEnumerable<CNode> x, IEnumerable<CNode> y)
{
if (x == null) return y == null; // true if both null
if (y == null) return false; // false because x not null
// optimizations: true if x and y are same object; false if different types
if (Object.ReferenceEquals(x, y) return true;
if (x.GetType() != y.GetType()) return false;
return x.SequenceEquals(y, this.CNodeComparer);
}