Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 按顺序元素对集合进行分组_C#_Linq - Fatal编程技术网

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个。谢谢,使用这样的扩展方法会很有趣。我试图找到允许不初始化集合的方法,但是,正如我所看到的,这在这个任务中是不可能的。所以,谢谢你这样简洁的方式