Rebase a 1-based array in c# - c#

I have an array in c# that is 1-based (generated from a call to get_Value for an Excel Range
I get a 2D array for example
object[,] ExcelData = (object[,]) MySheet.UsedRange.get_Value(Excel.XlRangeValueDataType.xlRangeValueDefault);
this appears as an array for example ExcelData[1..20,1..5]
is there any way to tell the compiler to rebase this so that I do not need to add 1 to loop counters the whole time?
List<string> RowHeadings = new List<string>();
string [,] Results = new string[MaxRows, 1]
for (int Row = 0; Row < MaxRows; Row++) {
if (ExcelData[Row+1, 1] != null)
RowHeadings.Add(ExcelData[Row+1, 1]);
...
...
Results[Row, 0] = ExcelData[Row+1, 1];
& other stuff in here that requires a 0-based Row
}
It makes things less readable since when creating an array for writing the array will be zero based.

Why not just change your index?
List<string> RowHeadings = new List<string>();
for (int Row = 1; Row <= MaxRows; Row++) {
if (ExcelData[Row, 1] != null)
RowHeadings.Add(ExcelData[Row, 1]);
}
Edit: Here is an extension method that would create a new, zero-based array from your original one (basically it just creates a new array that is one element smaller and copies to that new array all elements but the first element that you are currently skipping anyhow):
public static T[] ToZeroBasedArray<T>(this T[] array)
{
int len = array.Length - 1;
T[] newArray = new T[len];
Array.Copy(array, 1, newArray, 0, len);
return newArray;
}
That being said you need to consider if the penalty (however slight) of creating a new array is worth improving the readability of the code. I am not making a judgment (it very well may be worth it) I am just making sure you don't run with this code if it will hurt the performance of your application.

Create a wrapper for the ExcelData array with a this[,] indexer and do rebasing logic there. Something like:
class ExcelDataWrapper
{
private object[,] _excelData;
public ExcelDataWrapper(object[,] excelData)
{
_excelData = excelData;
}
public object this[int x, int y]
{
return _excelData[x+1, y+1];
}
}

Since you need Row to remain as-is (based on your comments), you could just introduce another loop variable:
List<string> RowHeadings = new List<string>();
string [,] Results = new string[MaxRows, 1]
for (int Row = 0, SrcRow = 1; SrcRow <= MaxRows; Row++, SrcRow++) {
if (ExcelData[SrcRow, 1] != null)
RowHeadings.Add(ExcelData[SrcRow, 1]);
...
...
Results[Row, 0] = ExcelData[SrcRow, 1];
}

Why not use:
for (int Row = 1; Row <= MaxRows; Row++) {
Or is there something I'm missing?
EDIT: as it turns out that something is missing, I would use another counter (starting at 0) for that purpose, and use a 1 based Row index for the array. It's not good practice to use the index for another use than the index in the target array.

Is changing the loop counter too hard for you?
for (int Row = 1; Row <= MaxRows; Row++)
If the counter's range is right, you don't have to add 1 to anything inside the loop so you don't lose readability. Keep it simple.

I agree that working with base-1 arrays from .NET can be a hassle. It is also potentially error-prone, as you have to mentally make a shift each time you use it, as well as correctly remember which situations will be base 1 and which will be base 0.
The most performant approach is to simply make these mental shifts and index appropriately, using base-1 or base-0 as required.
I personally prefer to convert the two dimensional base-1 arrays to two dimensional base-0 arrays. This, unfortunately, requires the performance hit of copying over the array to a new array, as there is no way to re-base an array in place.
Here's an extension method that can do this for the 2D arrays returned by Excel:
public static TResult[,] CloneBase0<TSource, TResult>(
this TSource[,] sourceArray)
{
If (sourceArray == null)
{
throw new ArgumentNullException(
"The 'sourceArray' is null, which is invalid.");
}
int numRows = sourceArray.GetLength(0);
int numColumns = sourceArray.GetLength(1);
TResult[,] resultArray = new TResult[numRows, numColumns];
int lb1 = sourceArray.GetLowerBound(0);
int lb2 = sourceArray.GetLowerBound(1);
for (int r = 0; r < numRows; r++)
{
for (int c = 0; c < numColumns; c++)
{
resultArray[r, c] = sourceArray[lb1 + r, lb2 + c];
}
}
return resultArray;
}
And then you can use it like this:
object[,] array2DBase1 = (object[,]) MySheet.UsedRange.get_Value(Type.Missing);
object[,] array2DBase0 = array2DBase1.CloneBase0();
for (int row = 0; row < array2DBase0.GetLength(0); row++)
{
for (int column = 0; column < array2DBase0.GetLength(1); column++)
{
// Your code goes here...
}
}
For massively sized arrays, you might not want to do this, but I find that, in general, it really cleans up your code (and mind-set) to make this conversion, and then always work in base-0.
Hope this helps...
Mike

For 1 based arrays and Excel range operations as well as UDF (SharePoint) functions I use this utility function
public static object[,] ToObjectArray(this Object Range)
{
Type type = Range.GetType();
if (type.IsArray && type.Name == "Object[,]")
{
var sourceArray = Range as Object[,];
int lb1 = sourceArray.GetLowerBound(0);
int lb2 = sourceArray.GetLowerBound(1);
if (lb1 == 0 && lb2 == 0)
{
return sourceArray;
}
else
{
int numRows = sourceArray.GetLength(0);
int numColumns = sourceArray.GetLength(1);
var resultArray = new Object[numRows, numColumns];
for (int r = 0; r < numRows; r++)
{
for (int c = 0; c < numColumns; c++)
{
resultArray[r, c] = sourceArray[lb1 + r, lb2 + c];
}
}
return resultArray;
}
}
else if (type.IsCOMObject)
{
// Get the Value2 property from the object.
Object value = type.InvokeMember("Value2",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.GetProperty,
null,
Range,
null);
if (value == null)
value = string.Empty;
if (value is string)
return new object[,] { { value } };
else if (value is double)
return new object[,] { { value } };
else
{
object[,] range = (object[,])value;
int rows = range.GetLength(0);
int columns = range.GetLength(1);
object[,] param = new object[rows, columns];
Array.Copy(range, param, rows * columns);
return param;
}
}
else
throw new ArgumentException("Not A Excel Range Com Object");
}
Usage
public object[,] RemoveZeros(object range)
{
return this.RemoveZeros(range.ToObjectArray());
}
[ComVisible(false)]
[UdfMethod(IsVolatile = false)]
public object[,] RemoveZeros(Object[,] range)
{...}
The first function is com visible and will accept an excel range or a chained call from another function (the chained call will return a 1 based object array), the second call is UDF enabled for Excel Services in SharePoint. All of the logic is in the second function. In this example we are just reformatting a range to replace zero with string.empty.

You could use a 3rd party Excel compatible component such as SpreadsheetGear for .NET which has .NET friendly APIs - including 0 based indexing for APIs such as IRange[int rowIndex, int colIndex].
Such components will also be much faster than the Excel API in almost all cases.
Disclaimer: I own SpreadsheetGear LLC

Related

Figure out max number of consecutive seats

I had an interviewer ask me to write a program in c# to figure out the max number of 4 members families that can sit consecutively in a venue, taking into account that the 4 members must be consecutively seated in one single row, with the following context:
N represents the number of rows availabe.
The Columns are labeled from the letter "A" to "K", purposely ommiting the letter "i" (in other words, {A,B,C,D,E,F,G,H,J,K})
M represents a list of reserved seats
Quick example:
N = 2
M = {"1A","2F","1C"}
Solution = 3
In the representation you can see that, with the reservations and the size given, only three families of 4 can be seated in a consecutive order.
How would you solve this? is it possible to not use for loops? (Linq solutions)
I got mixed up in the for loops when trying to deal with the reservations aray: My idea was to obtain all the reservations that a row has, but then I don't really know how to deal with the letters (Converting directly from letter to number is a no go because the missing "I") and you kinda need the letters to position the reserved sits anyway.
Any approach or insight on how to go about this problem would be nice.
Thanks in advance!
Here is another implementation.
I also tried to explain why certain things have been done.
Good luck.
private static int GetNumberOfAvailablePlacesForAFamilyOfFour(int numberOfRows, string[] reservedSeats)
{
// By just declaring the column names as a string of the characters
// we can query the column index by colulmnNames.IndexOf(char)
string columnNames = "ABCDEFGHJK";
// Here we transform the reserved seats to a matrix
// 1A 2F 1C becomes
// reservedSeatMatrix[0] = [0, 2] -> meaning row 1 and columns A and C, indexes 0 and 2
// reservedSeatMatrix[1] = [5] -> meaning row 2 and column F, index 5
List<List<int>> reservedSeatMatrix = new List<List<int>>();
for (int row = 0; row < numberOfRows; row++)
{
reservedSeatMatrix.Add(new List<int>());
}
foreach (string reservedSeat in reservedSeats)
{
int seatRow = Convert.ToInt32(reservedSeat.Substring(0, reservedSeat.Length - 1));
int seatColumn = columnNames.IndexOf(reservedSeat[reservedSeat.Length - 1]);
reservedSeatMatrix[seatRow - 1].Add(seatColumn);
}
// Then comes the evaluation.
// Which is simple enough to read.
int numberOfAvailablePlacesForAFamilyOfFour = 0;
for (int row = 0; row < numberOfRows; row++)
{
// Reset the number of consecutive seats at the beginning of a new row
int numberOfConsecutiveEmptySeats = 0;
for (int column = 0; column < columnNames.Length; column++)
{
if (reservedSeatMatrix[row].Contains(column))
{
// reset when a reserved seat is reached
numberOfConsecutiveEmptySeats = 0;
continue;
}
numberOfConsecutiveEmptySeats++;
if(numberOfConsecutiveEmptySeats == 4)
{
numberOfAvailablePlacesForAFamilyOfFour++;
numberOfConsecutiveEmptySeats = 0;
}
}
}
return numberOfAvailablePlacesForAFamilyOfFour;
}
static void Main(string[] args)
{
int familyPlans = GetNumberOfAvailablePlacesForAFamilyOfFour(2, new string[] { "1A", "2F", "1C" });
}
Good luck on your interview
As always, you will be asked how could you improve that? So you'd consider complexity stuff like O(N), O(wtf).
Underlying implementation would always need for or foreach. Just importantly, never do unnecessary in a loop. For example, if there's only 3 seats left in a row, you don't need to keep hunting on that row because it is not possible to find any.
This might help a bit:
var n = 2;
var m = new string[] { "1A", "2F", "1C" };
// We use 2 dimension bool array here. If it is memory constraint, we can use BitArray.
var seats = new bool[n, 10];
// If you just need the count, you don't need a list. This is for returning more information.
var results = new List<object>();
// Set reservations.
foreach (var r in m)
{
var row = r[0] - '1';
// If it's after 'H', then calculate index based on 'J'.
// 8 is index of J.
var col = r[1] > 'H' ? (8 + r[1] - 'J') : r[1] - 'A';
seats[row, col] = true;
}
// Now you should all reserved seats marked as true.
// This is O(N*M) where N is number of rows, M is number of columns.
for (int row = 0; row < n; row++)
{
int start = -1;
int length = 0;
for (int col = 0; col < 10; col++)
{
if (start < 0)
{
if (!seats[row, col])
{
// If there's no consecutive seats has started, and current seat is available, let's start!
start = col;
length = 1;
}
}
else
{
// If have started, check if we could have 4 seats.
if (!seats[row, col])
{
length++;
if (length == 4)
{
results.Add(new { row, start });
start = -1;
length = 0;
}
}
else
{
// // We won't be able to reach 4 seats, so reset
start = -1;
length = 0;
}
}
if (start < 0 && col > 6)
{
// We are on column H now (only have 3 seats left), and we do not have a consecutive sequence started yet,
// we won't be able to make it, so break and continue next row.
break;
}
}
}
var solution = results.Count;
LINQ, for and foreach are similar things. It is possible you could wrap the above into a custom iterator like:
class ConsecutiveEnumerator : IEnumerable
{
public IEnumerator GetEnumerator()
{
}
}
Then you could start using LINQ.
If you represent your matrix in simple for developers format, it will be easier. You can accomplish it either by dictionary or perform not so complex mapping by hand. In any case this will calculate count of free consecutive seats:
public static void Main(string[] args)
{
var count = 0;//total count
var N = 2; //rows
var M = 10; //columns
var familySize = 4;
var matrix = new []{Tuple.Create(0,0),Tuple.Create(1,5), Tuple.Create(0,2)}.OrderBy(x=> x.Item1).ThenBy(x=> x.Item2).GroupBy(x=> x.Item1, x=> x.Item2);
foreach(var row in matrix)
{
var prevColumn = -1;
var currColumn = 0;
var free = 0;
var div = 0;
//Instead of enumerating entire matrix, we just calculate intervals in between reserved seats.
//Then we divide them by family size to know how many families can be contained within
foreach(var column in row)
{
currColumn = column;
free = (currColumn - prevColumn - 1)/familySize;
count += free;
prevColumn = currColumn;
}
currColumn = M;
free = (currColumn - prevColumn - 1)/familySize;
count += free;
}
Console.WriteLine("Result: {0}", count);
}

enum ToString and performance

I was profiling some C# code and wound up investigating the implementation of ToString for enum values.
The ToString method ultimately calls System.Type.GetEnumName. Here's the relevant source code, taken from http://referencesource.microsoft.com/#mscorlib/system/type.cs
public virtual string GetEnumName(object value)
{
if (value == null)
throw new ArgumentNullException("value");
if (!IsEnum)
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
Contract.EndContractBlock();
Type valueType = value.GetType();
if (!(valueType.IsEnum || Type.IsIntegerType(valueType)))
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");
Array values = GetEnumRawConstantValues();
int index = BinarySearch(values, value);
if (index >= 0)
{
string[] names = GetEnumNames();
return names[index];
}
return null;
}
// Returns the enum values as an object array.
private Array GetEnumRawConstantValues()
{
string[] names;
Array values;
GetEnumData(out names, out values);
return values;
}
// This will return enumValues and enumNames sorted by the values.
private void GetEnumData(out string[] enumNames, out Array enumValues)
{
Contract.Ensures(Contract.ValueAtReturn<String[]>(out enumNames) != null);
Contract.Ensures(Contract.ValueAtReturn<Array>(out enumValues) != null);
FieldInfo[] flds = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
object[] values = new object[flds.Length];
string[] names = new string[flds.Length];
for (int i = 0; i < flds.Length; i++)
{
names[i] = flds[i].Name;
values[i] = flds[i].GetRawConstantValue();
}
// Insertion Sort these values in ascending order.
// We use this O(n^2) algorithm, but it turns out that most of the time the elements are already in sorted order and
// the common case performance will be faster than quick sorting this.
IComparer comparer = Comparer.Default;
for (int i = 1; i < values.Length; i++)
{
int j = i;
string tempStr = names[i];
object val = values[i];
bool exchanged = false;
// Since the elements are sorted we only need to do one comparision, we keep the check for j inside the loop.
while (comparer.Compare(values[j - 1], val) > 0)
{
names[j] = names[j - 1];
values[j] = values[j - 1];
j--;
exchanged = true;
if (j == 0)
break;
}
if (exchanged)
{
names[j] = tempStr;
values[j] = val;
}
}
enumNames = names;
enumValues = values;
}
// Convert everything to ulong then perform a binary search.
private static int BinarySearch(Array array, object value)
{
ulong[] ulArray = new ulong[array.Length];
for (int i = 0; i < array.Length; ++i)
ulArray[i] = Enum.ToUInt64(array.GetValue(i));
ulong ulValue = Enum.ToUInt64(value);
return Array.BinarySearch(ulArray, ulValue);
}
If I'm understanding this correctly, whenever an enum's name is requested:
The entire set of enum values and names are inserted into arrays using insertion sort.
The values are copied to an array of ulongs.
This array is searched with binary search.
Am right to think that this is grossly inefficient? (especially for enums with hundreds or thousands of values)
It seems like some sort of dictionary/hashtable structure would be ideal. But even a simple O(n) linear scan of the values would have accomplished the same goal without sorting or binary searching or allocating superfluous arrays.
I was able to precompute a dictionary for one particular case where ToString was being called many times in a loop; and that yielded significant performance gains. But is there any built-in, alternative, more holistic technique that would also facilitate things like JSON serialization?

Shifting A single element in an array

I want to shift one element in an array to the right each time whilst leaving the original elements in their specific order in C#.
Ok so I've been asked to reword the code I can understand why so here we go:
I might have a number 48390
//the ar elements have been commented out to show that we never know what ar contains but only the that I will always want to shift; ar[4]
int[] ar = new int[5];
//ar[0] = 4
//ar[1] = 8
//ar[2] = 3
//ar[3] = 9
//ar[4] = 0
while(ar != 04839)
{
Shift code
}
I might input 5 numbers 48390 if you notice its the same number but one digit is out. I want a while loop to rotate that 4 ar[1] to shift until the number forms 04839
I hope this makes sense. I am posting this question because most pages posting information about shifting based on shifting all elements to the right and I only really want to shift one specific element.
Thanks for looking.
edit: I should have been more specific. What if you don't know what each of the array elements could be? So I couldn't depend on "0" as an anchor. as another set of numbers might include another number for example "00238."
This method will give you a sequence of arrays made by inserting a single element into (between) each position in a given array:
public static IEnumerable<T[]> InsertElementBetweenAllPositions<T>(
T[] array, T element)
{
int newLength = array.Length + 1;
for (int i = 0; i < newLength; i++)
{
T[] rtn = new T[newLength];
rtn[i] = element;
Array.Copy(array, 0, rtn, 0, i);
Array.Copy(array, i, rtn, i + 1, array.Length - i);
yield return rtn;
}
}
For your example, you might call it as
foreach (int[] arr in InsertElementBetweenAllPositions(new[] { 6, 7, 8, 9 }, 0))
{
foreach (int i in arr)
Console.Write(i + " ");
Console.WriteLine();
}
How about this:
List<int> l = new List<int>(){0,6,7,8,9};
for (int i=1;i<5;i++)
{
l.Remove(0);
l.Insert(i, 0);
}
What's in your example is a swap, which can be implemented like:
private void Swap(ref int[] array, int index1, int index2)
{
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
Calling Swap(ref source, 0, 1) would exchange the first and second element. What you want then is:
for (int i = 0; i < a.Length-1; i++)
{
Swap(ref a, i, i+1);
}
This "bubbles" the first element up to the last position in each iteration.
From the example you need to shift elements around, and the example is a bit confusing over whether you need to loop them around to the start again. I have provided the below example that will loop around to the start - If you do not need to do that, the you can rework the if the statement.
private int[] Shift(int[] a)
{
int zeroPos = Array.IndexOf(a, 0);
int[] rtn = new int[a.Length];
a.CopyTo(rtn, 0);
if (zeroPos + 1 == a.Length)
{
rtn[0] = 0;
for (int i = 0; i < a.Length - 1; i++)
{
rtn[i + 1] = a[i];
}
}
else
{
rtn[zeroPos] = rtn[zeroPos + 1];
rtn[zeroPos + 1] = 0;
}
return rtn;
}
r=ar[0];
for (int i = 0; ar.lenght;i++)
{
ar[i]=ar[i + 1];
}
ar[ar.lenght] = r;
Have you thought about using a LinkedList instead? A linked list data structure is probably more suited to what you are trying to do than an array. The AddFirst, AddLast, AddAfter and AddBefore methods allow you to insert elements into the list in a much more efficient way than re-organizing your array each time.
The disadvantage of linked lists is that you need to read the elements in order. So, it's very efficient for inserting/deleting elements but inefficient for accessing elements randomly.
There is a good overview of LinkedLists here.
Perhaps
int oldLast = ar[ar.Length - 1];
for (int i = ar.Length - 1; i >= 0; i--)
ar[i] = i == 0 ? oldLast : ar[i - 1];
Demo
It is just a permutation of an item , below is the full source code of permutation algorithm.
static List<string> Put(char s1, string list)
{
List<string> str =new List<string>();
for (int i = 0; i < list.Length+1; i++)
{
string s = list.Substring(0, i) + s1.ToString() + list.Substring(i);
str.Add(s);
}
return str;
}
static List<string> Permute(string list,int x)
{
List<string> Result = new List<string>();
if (list.Length == 1)
{
Result.Add(list[0].ToString());
return Result;
}
else
{
char first = list[0];
list = list.Substring(x+1);
List<string> part = Permute(list,0);
foreach (string str in part)
{
List<string> hasBeenPlaced = Put(first, str);
foreach (string str2 in hasBeenPlaced)
{
Result.Add(str2);
}
}
}
return Result;
}
static void Main(string[] args)
{
List<string> per = Permute("abc",0);
for (int i = 0; i < per.Count; i++)
{
Console.WriteLine(per[i]);
}
Console.ReadKey();
}
Now if I add a break after the foreach , your problem has been solved . (it will writes all permuation for just an item which you want , not all of them....)
So change that to :
foreach (string str in part)
{
List<string> hasBeenPlaced = Put(first, str);
foreach (string str2 in hasBeenPlaced)
{
Result.Add(str2);
}
break;
}
Hope to helps you
If you you linq, that's simple :-) But you need a size larger than the array.
ShiftLeft(ar, 1);
private static int[] ShiftLeft(int[] value, int countOfShift = 1)
{
var length = value.Length;
if (countOfShift > length)
{
throw new InvalidOperationException("countOfShift must less then value's length.");
}
var tempList = new List<int>(value);
tempList.RemoveRange(length - countOfShift, countOfShift);
tempList.InsertRange(0, value.Skip(length - countOfShift));
return tempList.ToArray();
}

Fastest way to convert T[,] to T[][]?

So it turns out all arrays are not created equal. Multi-dimensional arrays can have non-zero lower bounds. See for example Excel PIA's Range.Value property object[,] rectData = myRange.Value;
I need to convert these data into a jagged array. My first try below smells of complexity. Any suggestions to optimize it? It needs to handle the general case where lower bounds may not be zero.
I have this ex method:
public static T[][] AsJagged<T>( this T[,] rect )
{
int row1 = rect.GetLowerBound(0);
int rowN = rect.GetUpperBound(0);
int col1 = rect.GetLowerBound(1);
int colN = rect.GetUpperBound(1);
int height = rowN - row1 + 1;
int width = colN - col1 + 1;
T[][] jagged = new T[height][];
int k = 0;
int l;
for ( int i = row1; i < row1 + height; i++ )
{
l = 0;
T[] temp = new T[width];
for ( int j = col1; j < col1 + width; j++ )
temp[l++] = rect[i, j];
jagged[k++] = temp;
}
return jagged;
}
Used like this:
public void Foo()
{
int[,] iRect1 = { { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 } };
int[][] iJagged1 = iRect1.AsJagged();
int[] lengths = { 3, 5 };
int[] lowerBounds = { 7, 8 };
int[,] iRect2 = (int[,])Array.CreateInstance(typeof(int), lengths, lowerBounds);
int[][] iJagged2 = iRect2.AsJagged();
}
Curious if Buffer.BlockCopy() would work or be faster?
Edit: AsJagged needs to handle reference types.
Edit: Found bug in AsJagged(). Added int l; and added col1 + width to inner loop.
A view caveats/assumptions up front:
You seem to use only int as your data type (or at least seem to be OK with using Buffer.BlockCopy which would imply you can life with primitive types in general).
For the test data you show, I don't think there will be much different using any somewhat sane approach.
Having that said, the following implementation (which needs to be specialized for a specific primitive type (here int) because it uses fixed) is around 10 times faster than the approach using the inner loop:
unsafe public static int[][] AsJagged2(int[,] rect)
{
int row1 = rect.GetLowerBound(0);
int rowN = rect.GetUpperBound(0);
int col1 = rect.GetLowerBound(1);
int colN = rect.GetUpperBound(1);
int height = rowN - row1 + 1;
int width = colN - col1 + 1;
int[][] jagged = new int[height][];
int k = 0;
for (int i = row1; i < row1 + height; i++)
{
int[] temp = new int[width];
fixed (int *dest = temp, src = &rect[i, col1])
{
MoveMemory(dest, src, rowN * sizeof(int));
}
jagged[k++] = temp;
}
return jagged;
}
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
unsafe internal static extern void MoveMemory(void* dest, void* src, int length);
Using the following "test code":
static void Main(string[] args)
{
Random rand = new Random();
int[,] data = new int[100,1000];
for (int i = 0; i < data.GetLength(0); i++)
{
for (int j = 0; j < data.GetLength(1); j++)
{
data[i, j] = rand.Next(0, 1000);
}
}
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
int[][] dataJagged = AsJagged(data);
}
Console.WriteLine("AsJagged: " + sw.Elapsed);
sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
int[][] dataJagged2 = AsJagged2(data);
}
Console.WriteLine("AsJagged2: " + sw.Elapsed);
}
Where AsJagged (the first case) is your original function, I get the following output:
AsJagged: 00:00:00.9504376
AsJagged2: 00:00:00.0860492
So there is indeed a faster way of doing it, however depending on the size of the test data, the number of times you actually perform this operation, and your willingness to allow unsafe and P/Invoke code, you're probably not going to need it.
Having that said, we were using large matrixes of double (say 7000x10000 elements) where it indeed did make a huge difference.
Update: about using Buffer.BlockCopy
I might overlook some Marshal or other trick, but I don't think using Buffer.BlockCopy is possible here. This is due to the fact that it requires both the source and destination array to, well, be an Array.
In our example, the destination is an array (e.g. int[] temp = ...) however the source is not. While we "know" that for two dimensional arrays of primitive types the layout is such, that each "row" (i.e. first dimension) is an array of the type in memory, there is no safe (as in unsafe) way to get that array without the overhead of copying it first. So we basically need to use a function that simply deals with memory and doesn't care about the actual content of it - like MoveMemory. BTW, the internal implementation of Buffer.BlockCopy does something similar.
Your complexity is O(N*M) N - number of rows, M - number of columns. That's the best you can get when copying N*M values...
Buffer.BlockCopy might be faster than your inner for loop, but I wouldn't be surprised if the compiler knows how to handle this code properly and you won't gain any further speed. You should test it to make sure.
You may be able to achieve better performance by not copying the data at all (at the potential expense of slightly slower lookups). If you create an 'array row' class, that holds your rect and a row number, and provides an indexer that accesses the correct column, you can create an array of such rows, and save yourself the copying altogether.
The complexity of creating such an array of 'array rows' is O(N).
EDIT: An ArrayRow class, just because it bugs me...
The ArrayRow could look something like this:
class ArrayRow<T>
{
private T[,] _source;
private int _row;
public ArrayRow(T[,] rect, int row)
{
_source = rect;
_row = row;
}
public T this[int col] { get { return _source[_row, col]; } }
}
Now you create an array of ArrayRows, you don't copy anything at all, and the optimizer has a good chance of optimizing accessing an entire row in sequence.

Getting a double[] row array of a double[,] rectangular array

Suppose you have an array like:
double[,] rectArray = new double[10,3];
Now you want the fouth row as a double[] array of 3 elements without doing:
double[] fourthRow = new double[]{rectArray[3,0],
rectArray[3,1],
rectArray[3,2]};
Is it possible someway? Even using a Marshal.Something approach?
Thanks!
You can use Buffer.BlockCopy method:
const int d1 = 10;
const int d2 = 3;
const int doubleSize = 8;
double[,] rectArray = new double[d1, d2];
double[] target = new double[d2];
int rowToGet = 3;
Buffer.BlockCopy(rectArray, doubleSize * d2 * rowToGet, target, 0, doubleSize * d2);
LINQ to the rescue:
var s = rectArray.Cast<double>().Skip(9).Take(3).ToArray();
Explanation: Casting a multi-dimensional array flattens it to a single-dimensional array. After that all we need to do is skip to the element we want (the 4th element in the 2-D array resolves to Skip(9)...) and take 3 elements from it).
Why not make a generic extension method?
public static T[] GetRow<T>(this T[,] input2DArray, int row) where T : IComparable
{
var width = input2DArray.GetLength(0);
var height = input2DArray.GetLength(1);
if (row >= height)
throw new IndexOutOfRangeException("Row Index Out of Range");
// Ensures the row requested is within the range of the 2-d array
var returnRow = new T[width];
for(var i = 0; i < width; i++)
returnRow[i] = input2DArray[i, row];
return returnRow;
}
Like this all you have to code is:
array2D = new double[,];
// ... fill array here
var row = array2D.GetRow(4) // Implies getting 5th row of the 2-D Array
This is useful if you're trying to chain methods after obtaining a row and could be helpful with LINQ commands as well.
You probably want to use a jagged array. That is not an array of 10 by 3 but instead an array of arrays.
Something like :
double[][] rectArray;
....
double [] rowArray = rectArray[3];
There are lots of places to learn more about jagged arrays. For example Dynamically created jagged rectangular array
If you must use a rectangular array and just want to simplify the syntax, you can use a method to get the row like so:
double[] fourthRow = GetRow(rectArray, 3);
public static T[] GetRow<T>(T[,] matrix, int row)
{
var columns = matrix.GetLength(1);
var array = new T[columns];
for (int i = 0; i < columns; ++i)
array[i] = matrix[row, i];
return array;
}
Although this is an old thread, an addition to Joseph Sturtevants answer may be useful. His function crashes in case the matrix's first column is not zero, but another integer.
This is e.g. always the case in case of retrieving data from Excel, like
object[,] objects = null;
Excel.Range range = worksheet.get_Range("A1", "C5");
objects = range.Cells.Value; //columns start at 1, not at 0
The GetRow function could be modified like this:
public static T[] GetRow<T>(T[,] matrix, int row, int firstColumn)
{
var columns = matrix.GetLength(1);
var array = new T[columns];
for (int i = firstColumn; i < firstColumn + columns; ++i)
array[i-firstColumn] = matrix[row, i];
return array;
}

Categories