Split Array By Values In Sequence [duplicate]

2020-01-19 07:14发布

问题:

Is there an easy (linq?) way to split an int array into new arrays based off unbroken numerical sequences? For example given this pseudo code:

[Fact]
public void ArraySpike()
{
   var source = new[] {1, 2, 3, 7, 8, 9, 12, 13, 24};

   var results = SplitArray(source);

    Assert.True(results[0] == new[] {1, 2, 3});
    Assert.True(results[1] == new[] {7, 8, 9}); 
    Assert.True(results[2] == new[] {12, 13}); 
    Assert.True(results[3] == new[] {24}); 
}

public int[][] SplitArray(int[] source)
{
    return source.???   
}

回答1:

This can work with the linq extension Aggregate. My seeding is not very elegant but that is easy enough to change. The results variable will contain the array of arrays and they are actually of type List<T> because that way they can be easily grown in the function where an array [] is always of fixed size.

This also assumes the source is already ordered and unique, if that is not the case add .OrderBy(x => x).Distinct()

var source = new[] { 1, 2, 3, 7, 8, 9, 12, 13, 24 };
var results = new List<List<int>>{new List<int>()};

var temp = source.Aggregate(results[0], (b, c) =>
{
    if (b.Count > 0 && b.Last() != c - 1)
    {
        b = new List<int>();
        results.Add(b);
    }
    b.Add(c);
    return b;
});


回答2:

I dug up this extension method from my personal collection:

public static IEnumerable<IEnumerable<T>> GroupConnected<T>(this IEnumerable<T> list, Func<T,T,bool> connectionCondition)
{
    if (list == null)
    {
        yield break;
    }
    using (var enumerator = list.GetEnumerator())
    {
        T prev = default(T);
        var temp = new List<T>();
        while (enumerator.MoveNext())
        {
            T curr = enumerator.Current;
            {
                if(!prev.Equals(default(T)) && !connectionCondition(prev, curr))
                {
                    yield return temp;
                    temp = new List<T>();
                }
                temp.Add(curr);
            }
            prev = curr;
        }
        yield return temp;
    }
}

It solves the problem in a more general sense: split up a sequence in subsequences of elements that are "connected" somehow. It traverses the sequence and collects each element in a temporary list until the next item isn't "connected". It then returns the temporary list and begins a new one.

Your array elements are connected when they have a difference of 1:

var results = source.GroupConnected((a,b) => b - a == 1);