Reputation: 7536
There's an array:
var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
Is there a simple way to split it into arrays of the same values?
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 } };
I would prefer a linq solution but couldn't find it at the first time.
Upvotes: 0
Views: 902
Reputation: 9377
An extension method like the answer by @L.B but a little bit more functional oriented:
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));
}
Note that the extension method uses the Microsoft Immutable Collections
library. The library can be downloaded through NuGet
.
Usage:
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);
Output:
Array 0 = 1,1
Array 1 = 2
Array 2 = 6,6
Array 3 = 7
Array 4 = 1,1
Array 5 = 0
Benchmark
Just for the sake of fun, I tried to benchmark the answers.
I used the following code:
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);
And got the following results:
-------------------------------------
| 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) |
-------------------------------------
The slight time overhead from my solution (the 41ms) is due to using immutable collections. Adding an item to ex. List<T>
would modify the List<T>
object. - Adding an item to ImmutableList<T>
clones the current elements in it, and adds them to a new ImmutableList<T>
along with the new item (which results in a slight overhead).
Upvotes: 1
Reputation: 2042
This will do the trick:
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();
Basically this will generate an array for each sequence item with a length of 1 or greater and will only choose the arrays which correspond to an item in the original sequence which is either the first element or an element that differs from its predecessor.
Upvotes: 2
Reputation: 101681
You can try this:
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();
Upvotes: 1
Reputation: 116138
I would write an extension method for this:
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;
}
}
}
}
and use it as
var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
var result = arr.GroupSequenceWhile((x, y) => x == y).ToList();
Upvotes: 5
Reputation: 82096
var grouped = arr.GroupBy(x => x).Select(x => x.ToArray())
Didn't notice you were after neighbouring groups initially, the following should work for that
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;
}
Upvotes: 3