将数组拆分为数组数组

时间:2014-08-02 10:22:41

标签: c# .net linq

有一个数组:

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解决方案但是第一次找不到它。

5 个答案:

答案 0 :(得分:5)

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

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();

答案 1 :(得分:3)

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;
}

Live example

答案 2 :(得分:2)

这样可以解决问题:

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或更大的数组,并且只选择与原始序列中的项对应的数组,该项是第一个元素或与其前一个元素不同的元素。

答案 3 :(得分: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();

答案 4 :(得分:1)

一种扩展方法,比如@ 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>(这会产生轻微的开销)。