Reputation: 31
Suppose I have an array of integers containing the following: [1,2,2,3,1,5,4,4,1,1,1]
How would I write a script that would divide the array into sub-arrays based on repeated neighbors ?
For example: [1],[2,2],[3],[1],[5],[4,4],[1,1,1]
Yet even better would be to receive an array of tuples denoting the value, position of first occurrence,& number of times repeated, for example: [ (1, 0, 1), (2, 1, 2), (3, 3, 1), (1, 4, 1) (5, 5, 1), (4, 6, 2), (1, 8, 3) ]
Here is what I tried writing originally but it doesn't seem to work right:
var array = handStacks;
var analysis = new List<(int, int, int)>();
var subArrayStart = 0;
var subArraySize = 0;
for(var x=0; x < array.Length; x++){
var curr = array[x];
// Last element
if(x == array.Length - 1){
if(subArrayStart == 0) subArraySize++;
analysis.Add( (subArraySize, array[subArrayStart], subArrayStart) );
}
else if(curr != array[subArrayStart]) { // New sub array
analysis.Add( (subArraySize, array[subArrayStart], subArrayStart) );
subArraySize = 1;
subArrayStart = x;
}
subArraySize++;
}
Upvotes: 2
Views: 167
Reputation: 23258
This code will give you the expected list of (int, int, int)
tuple. At every loop iteration you are creating a tuple instance and increment the element occurrence (until it's the same)
var array = new[] { 1, 2, 2, 3, 1, 5, 4, 4, 1, 1, 1 };
var analysis = new List<(int, int, int)>();
for (int i = 0; i < array.Length; i++)
{
var item = (value: array[i], position: i, occurence: 1);
var index = i + 1;
while (index < array.Length && array[index++] == item.value)
{
item.occurence++;
i++;
}
analysis.Add(item);
}
Console.WriteLine(string.Join(", ", analysis));
And it'll print
(1, 0, 1), (2, 1, 2), (3, 3, 1), (1, 4, 1), (5, 5, 1), (4, 6, 2), (1, 8, 3)
Upvotes: 1
Reputation: 342
For your "even better solution" I would suggest:
int[] arr = { 1, 2, 2, 3, 1, 5, 4, 4, 1, 1, 1 };
var targetsList = new List<(int,int,int)>();
int currentValue = arr[0];
int position = 0;
int repeats =0;
for (int i = 0; i < arr.Length; i++)
{
if (arr[i]!=currentValue)
{
targetsList.Add((currentValue,position,repeats));
position = i;
currentValue = arr[i];
repeats = 0;
}
repeats++;
}
targetsList.Add((currentValue,position,repeats));
foreach(var target in targetsList)
{
Console.WriteLine($"Value {target.Item1},Position {target.Item2},Repeats {target.Item3}");
}
Upvotes: 2
Reputation: 272895
Here is a more generic solution, using yield return
:
public static IEnumerable<(T, int, int)> Group<T>(IEnumerable<T> source) where T: IEquatable<T> {
if (!source.Any()) { // if input is empty, return empty sequence
yield break;
}
// element of current sub array
T currentElement = source.First();
int startIndex = 0; // ...of current sub array
int currentIndex = 0; // ...of the input sequence
int count = 0; // ...of current sub array
foreach(T elem in source) {
if (elem.Equals(currentElement)) {
count++;
} else {
// when we find a different element, return everything we know about the current sub array
yield return (currentElement, startIndex, count);
// reset info about the current sub array
currentElement = elem;
startIndex = currentIndex;
count = 1;
}
currentIndex++;
}
// after we iterated the whole input sequence, we return another sub array
yield return (currentElement, startIndex, count);
}
Usage:
List<(int, int, int)> result = Group(yourInputArray).ToList();
Upvotes: 1
Reputation: 45977
Approach with a simple for
loop
public static int[][] GroupBySequences(int[] items)
{
List<List<int>> result = new List<List<int>>();
List<int> currentArray = new List<int> { items[0] };
for (int i = 1; i < items.Length; i++)
{
if (items[i] != items[i - 1])
{
result.Add(currentArray);
currentArray = new List<int>();
}
currentArray.Add(items[i]);
}
result.Add(currentArray);
return result.Select(x => x.ToArray()).ToArray();
}
Upvotes: 1