将数组拆分为数组的数组

本文关键字:数组 拆分 | 更新日期: 2023-09-27 18:16:16

有一个数组:

var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };

有没有一种简单的方法可以将其拆分为具有相同值的数组?

var arrs = new int[][] { 
            new int[] { 1, 1 },
            new int[] { 2 },
            new int[] { 6, 6 },
            new int[] { 7 }, 
            new int[] { 1, 1 }, 
            new int[] { 0 } };

我更喜欢linq解决方案,但第一时间找不到。

将数组拆分为数组的数组

我会为此编写一个扩展方法:

public static class SOExtensions
{
    public static IEnumerable<IEnumerable<T>> GroupSequenceWhile<T>(this IEnumerable<T> seq, Func<T, T, bool> condition) 
    {
        List<T> list = new List<T>();
        using (var en = seq.GetEnumerator())
        {
            if (en.MoveNext())
            {
                var prev = en.Current;
                list.Add(en.Current);
                while (en.MoveNext())
                {
                    if (condition(prev, en.Current))
                    {
                        list.Add(en.Current);
                    }
                    else
                    {
                        yield return list;
                        list = new List<T>();
                        list.Add(en.Current);
                    }
                    prev = en.Current;
                }
                if (list.Any())
                    yield return list;
            }
        }
    }
}

并将其用作

var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
var result = arr.GroupSequenceWhile((x, y) => x == y).ToList();
var grouped = arr.GroupBy(x => x).Select(x => x.ToArray())

最初没有注意到你在邻近的组之后,以下内容应该适用于该

var arr = new[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
var groups = new List<int[]>();
for (int i = 0; i < arr.Length; i++)
{
    var neighours = arr.Skip(i).TakeWhile(x => arr[i] == x).ToArray();
    groups.Add(neighours);
    i += neighours.Length-1;
}

实例

这将完成任务:

var arrs = arr.Select((x, index) =>
    {
        var ar = arr.Skip(index)
            .TakeWhile(a => a == x)
            .ToArray();
        return ar;
    }).Where((x, index) => index == 0 || arr[index - 1] != arr[index]).ToArray();

基本上,这将为每个长度为1或更大的序列项生成一个数组,并且将只选择与原始序列中的一个项相对应的数组,该项是第一个元素或与其前一个元素不同的元素。

你可以试试这个:

int index = 0;
var result = arr.Select(number =>
            {
                var ar = arr.Skip(index)
                    .TakeWhile(a => a == number)
                    .ToArray();
                index += ar.Length;
                return ar;
            }).Where(x => x.Any()).ToArray();

类似@L的答案的扩展方法。B,但更注重功能:

public static IEnumerable<IEnumerable<T>> GroupWhile<T>(this IEnumerable<T> source, Func<T, T, bool> func)
{
    var firstElement = source.FirstOrDefault();
    return firstElement == null ? Enumerable.Empty<IEnumerable<T>>() : source.Skip(1).Aggregate(new
    {
        current = Tuple.Create(firstElement, ImmutableList<T>.Empty.Add(firstElement)),
        results = ImmutableList<ImmutableList<T>>.Empty,
    }, (acc, x) =>
        func(acc.current.Item1, x)
        ? new { current = Tuple.Create(x, acc.current.Item2.Add(x)), results = acc.results }
        : new { current = Tuple.Create(x, ImmutableList<T>.Empty.Add(x)), results = acc.results.Add(acc.current.Item2) }, 
        x => x.results.Add(x.current.Item2).Select(r => r));
}

请注意,扩展方法使用Microsoft Immutable Collections库。该库可以通过NuGet下载。

用法:

var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
var result = arr.GroupWhile((prev, current) => prev == current);
var printFormattedResult = result.Select((x, i) => Tuple.Create(i, string.Join(",", x)));
foreach (var array in printFormattedResult)
    Console.WriteLine("Array {0} = {1}", array.Item1, array.Item2);

输出:

Array 0 = 1,1
Array 1 = 2
Array 2 = 6,6
Array 3 = 7
Array 4 = 1,1
Array 5 = 0

基准

只是为了好玩,我试着对答案进行基准测试。

我使用了以下代码:

var rnd = new Random();
var arr = Enumerable.Range(0, 100000).Select(x => rnd.Next(10)).ToArray();
var time = Stopwatch.StartNew();
var result = <answer>.ToArray();
Console.WriteLine(t.ElapsedMilliseconds);

并得到以下结果:

-------------------------------------
| Solution  Time(ms)    Complexity  |
------------------------------------|
| L.B       | 3ms       | O(n)      |
|-----------------------------------|
|´ebb       | 41ms      | O(n)      |
|-----------------------------------|
| James     | 137ms     | O(n^2)    |
|-----------------------------------|
| Robert S. | 155ms     | O(n^2)    |
|-----------------------------------|
| Selman22  | 155ms     | O(n^2)    |
-------------------------------------

我的解决方案带来的轻微时间开销(41ms(是由于使用了不可变的集合。在ex.List<T>中添加一个项会修改List<T>对象。-向ImmutableList<T>中添加一个项目会克隆其中的当前元素,并将它们与新项目一起添加到新的ImmutableList<T>中(这会导致轻微的开销(。