I\'m trying to reorganize an array based on the first occurrence of a value (thus simulating similar functionality to a circular array.)
For example, in the followin
var ar = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ar = ar.SkipWhile(a => a != 6).ToArray<int>();
You could do the following:
Array.Copy()
to copy everything from start index to end of source array to destination arrayArray.Copy()
to copy everything from 0 to start index of source array to the end of the destination arrayThat way you get a copy of your source array that looks as you expected.
You'll have to play with various overloads of Array.Copy()
, however, because I don't know the exact parameter values right now.
To begin with, do a linear search to find the first occurrence of the value that you want to make the first element:
// value contains the value to find.
int skip;
for (int i = 0; i < array.Length; i++)
{
if (array[i] == value)
{
skip = i;
break;
}
}
// skip contains the index of the element to put at the front.
// Equivalently, it is the number of items to skip.
// (I chose this name for it because it makes the subtractions
// in the Array.Copy implementation more intuitive.)
Do you want to change the actual array? Then do what Thorsten Dittmar suggests:
int[] array = new int[] { 2, 3, 6, 1, 7, 6 };
int[] result = new int[array.Length];
int skip = 2; // So that array[skip] will be result[0] at the end
Array.Copy(array, skip, result, 0, array.Length - skip);
Array.Copy(array, 0, result, array.Length - skip, skip);
Do you want to just view the array in the new order, without doing anything else? Then index it like so:
array[(i + skip) % array.Length] // Instead of array[i]
Edit: Just for laughs, an implementation of Jon Skeet's suggestion to implement the copy while using only a single buffer value (sourceValue
):
// GCD gives the greatest common divisor
int gcd = GCD(array.Length, skip);
// period is the length of the permutation cycles in our rotation.
int period = array.Length / gcd;
int max = array.Length / period;
for (int i = 0; i < max; i++)
{
int sourceIndex = i;
int sourceValue = array[sourceIndex];
for (int n = 1; n <= period; n++)
{
int destinationIndex = (sourceIndex + array.Length - skip) % array.Length;
int temp = array[destinationIndex];
array[destinationIndex] = sourceValue;
sourceValue = temp;
sourceIndex = destinationIndex;
}
}
New approach that will only work with .NET 4.5 (from 2012) or later:
const int value = 6;
int[] myArray = { 2, 3, 6, 1, 7, 6, };
var index = Array.IndexOf(myArray, value);
if (index == -1)
throw new InvalidOperationException();
var rotatedArray = (new ArraySegment<int>(myArray, index, myArray.Length - index))
.Concat(new ArraySegment<int>(myArray, 0, index))
.ToArray();
In earlier .NET versions, an ArraySegment<>
value could not be used as an IEnumerable<>
like that.
Not clear to me if it was a requirement that the original array instance be mutated, but if you need that, simply append:
rotatedArray.CopyTo(myArray, 0);
to my code.
int[] myArray = { 2, 3, 6, 1, 7, 6 };
myArray = myArray
.SkipWhile(i => i != 6)
.Concat(myArray.TakeWhile(i => i != 6))
.ToArray();
Should do the trick!
You will need a using System.Linq
;
Thorsten's solution creates a new array; here's an in place version which only creates a temporary array as large as the amount your rotation size:
public static void RotateLeft<T>(T[] array, int places)
{
T[] temp = new T[places];
Array.Copy(array, 0, temp, 0, places);
Array.Copy(array, places, array, 0, array.Length - places);
Array.Copy(temp, 0, array, array.Length - places, places);
}
I'm sure it could be done with just a single temporary buffer item, but it would be more complicated :)
As an efficiency measure, here's a "rotate left one place" shortcut:
public static void RotateLeft<T>(T[] array)
{
T temp = array[0];
Array.Copy(array, 0, array, 1, array.Length - 1);
array[array.Length-1] = temp;
}