C# 按顺序元素对集合进行分组
如果我有一些集合,例如:整数集合:C# 按顺序元素对集合进行分组,c#,linq,C#,Linq,如果我有一些集合,例如:整数集合: 1 1 2 2 3 3 1 2 3 4 4 1 1 2 3 我只需要对连续元素进行分组,在本例中: 1 1 2 2 3 3 1 2 3 4 4 1 1 2 3 我构建了一个LINQ表达式,它创建了一个匿名集合,集合中有元素的索引,它是相等标记,然后我加入了集合,它向前移动了一个位置,然后对元素进行分组。但是这个算法,我认为,太过分了 还有更优雅的解决方案吗?如果您真的不需要LINQ,只需简单的循环即可: var res = new List<List
1 1 2 2 3 3 1 2 3 4 4 1 1 2 3
我只需要对连续元素进行分组,在本例中:
1 1
2 2
3 3
1
2
3
4 4
1 1
2
3
我构建了一个LINQ表达式,它创建了一个匿名集合,集合中有元素的索引,它是相等标记,然后我加入了集合,它向前移动了一个位置,然后对元素进行分组。但是这个算法,我认为,太过分了
还有更优雅的解决方案吗?如果您真的不需要LINQ,只需简单的循环即可:
var res = new List<List<int>>();
foreach(int i in data)
{
var c = res.Count;
if (c == 0 || res[c - 1][0] != i)
res.Add(new List<int>() { i });
else
res[c - 1].Add(i);
}
var res=new List();
foreach(数据中的int i)
{
var c=分辨率计数;
如果(c==0 | | res[c-1][0]!=i)
res.Add(新列表(){i});
其他的
res[c-1]。添加(i);
}
您也可以将linq与外部变量一起使用,但这可能更难阅读编辑:我找到了一种使用聚合扩展方法实现这一点的linq方法。此方法允许您使用Func lambda中的acculator参数查看集合中以前的项。这是:
static List<List<int>> Group2(List<int> data)
{
return data.Aggregate(new List<List<int>>(), (list, item) =>
{
if (list.Count == 0 || list[list.Count - 1][0] != item)
{
list.Add(new List<int> { item });
}
else
{
list[list.Count - 1].Add(item);
}
return list;
});
}
静态列表组2(列表数据)
{
返回数据。聚合(新列表(),(列表,项目)=>
{
if(list.Count==0 | | list[list.Count-1][0]!=项)
{
添加(新列表{item});
}
其他的
{
列表[list.Count-1]。添加(项);
}
退货清单;
});
}
我想不出一种Linq方法来实现这一点,因为我认为它的任何方法都不允许您以这种方式查看前面的项目。这里有一个通用的方法:
static IEnumerable<List<T>> Group<T>(IEnumerable<T> list, IEqualityComparer<T> comp)
{
T previous = default(T);
bool previousExists = false;
var eee = list.GetEnumerator();
List<T> result = null;
while(eee.MoveNext())
{
T current = eee.Current;
if (previousExists && comp.Equals(current, previous))
{
result.Add(current);
}
else
{
if (result != null)
yield return result;
result = new List<T> { current };
}
previous = current;
previousExists = true;
}
if (result != null)
yield return result;
}
静态IEnumerable组(IEnumerable列表,IEqualityComparer comp)
{
T先前=默认值(T);
bool previousExists=false;
var eee=list.GetEnumerator();
列表结果=空;
while(eee.MoveNext())
{
T电流=eee电流;
如果(以前存在且公司等于(当前、以前))
{
结果。添加(当前);
}
其他的
{
如果(结果!=null)
收益结果;
结果=新列表{current};
}
先前=当前;
previousExists=true;
}
如果(结果!=null)
收益结果;
}
以下是使用LINQ的实现:
//Input sequence
int[] input = new int[] { 1, 1, 2, 2, 3, 3, 1, 2, 3, 4, 4, 1, 1, 2, 3 };
//Group number
int i = 0;
//Result array [<group number>][]
int[][] values =
//Select new anonymous object, which contains the source value from input and its group number
input.Select((item, index) => new { Key = index > 0 ? (item == input[index - 1] ? i : ++i) : 0, Value = item })
//Group anonymous objects by group number
.GroupBy(pair => pair.Key)
//Select values for each group
.Select(g => g.Select(x => x.Value).ToArray())
.ToArray();
//输入序列
int[]input=newint[]{1,1,2,2,3,3,1,2,3,4,4,1,1,2,3};
//组号
int i=0;
//结果数组[][]
int[][]值=
//选择新的匿名对象,该对象包含输入的源值及其组号
输入.Select((项,索引)=>new{Key=index>0?(项==input[index-1]?i:++i):0,值=item})
//按组号对匿名对象分组
.GroupBy(pair=>pair.Key)
//为每个组选择值
.Select(g=>g.Select(x=>x.Value).ToArray()
.ToArray();
问题在于LINQ缺少一个Select
方法,该方法允许您点击上一个结果
如果是的话,你只需要写这样的东西:
var sequences = items
.SelectWithPreviousResult(
new { Item = -1, GroupNumber = 0 }, // default result (used for first item)
(item, previous) => new
{
Item = item,
GroupNumber = previous.Item == x
? previous.GroupNumber
: previous.GroupNumber + 1 })
.GroupBy(x => x.GroupNumber, x => x.Item);
public static class LinqExtensions
{
public static IEnumerable<TResult> SelectWithPreviousResult<TSource, TResult>(
this IEnumerable<TSource> items,
TResult defaultResult,
Func<TSource, TResult, TResult> func)
{
var previousResult = defaultResult;
foreach (var item in items)
{
var result = func(item, previousResult);
previousResult = result;
yield return result;
}
}
}
这样做的目的是选择当前项目以及一个GroupNumber
,该编号从0开始,仅当当前项目与上一个项目不同时才递增。然后它按GroupNumber
分组,并将组成员简化为项目
当然,上述内容不会编译,因为SelectWithPrevious
尚不存在。不过,LINQ的好处在于,您可以非常轻松地编写自己的扩展方法。SelectWithPreviousResult
方法的实现方式如下:
var sequences = items
.SelectWithPreviousResult(
new { Item = -1, GroupNumber = 0 }, // default result (used for first item)
(item, previous) => new
{
Item = item,
GroupNumber = previous.Item == x
? previous.GroupNumber
: previous.GroupNumber + 1 })
.GroupBy(x => x.GroupNumber, x => x.Item);
public static class LinqExtensions
{
public static IEnumerable<TResult> SelectWithPreviousResult<TSource, TResult>(
this IEnumerable<TSource> items,
TResult defaultResult,
Func<TSource, TResult, TResult> func)
{
var previousResult = defaultResult;
foreach (var item in items)
{
var result = func(item, previousResult);
previousResult = result;
yield return result;
}
}
}
公共静态类LinqExtensions
{
公共静态IEnumerable SelectWithPreviousResult(
这是数不清的项目,
TResult defaultResult,
Func Func)
{
var previousResult=defaultResult;
foreach(项目中的var项目)
{
var结果=func(项目,以前的结果);
先前的结果=结果;
收益结果;
}
}
}
以这种方式实现它的优点是,您可以重用扩展方法来解决类似的问题,并且与使用纯循环或复杂的
聚合表达式相比,您的代码获得了一些可读性。您可以显示代码吗?如果一行中有两个以上相等的元素,您的算法是否有效?它会将12
分为11
和2
?或者它会将其分为11
,11
,2
?@YacoubMassad@user2023861它必须处理任意数量的序列elements@dotFive,我之所以这么问,是因为听起来你提出的算法将集合向前移动一个位置,将3个集合视为2个集合中的2个。谢谢,使用这样的扩展方法会很有趣。我试图找到允许不初始化集合的方法,但是,正如我所看到的,这在这个任务中是不可能的。所以,谢谢你这样简洁的方式