c# array function argument pass one dimension - c#

Let say I got this function :
void int Calculate(double[] array) {}
And in my main I got this array:
double[,] myArray = new double[3,3];
How can I call Calculate(...) ?
I try (that's don't compile) :
double[] mySingleArray = myArray[0];
What I want to avoid is unnecessary loop (for).
I declare a regular array, but if a jagged array or any other type of array works better, it's fine for me.
I use c# 3.5

First, let's declare your Calculate() method like this:
int Calculate(IEnumerable<double> doubles)
Don't worry, you can still pass an array to that code. You might also need IList<double>, but 9 times out of 10 the IEnumerable is good enough. The main thing is that this will let us use the yield keyword to slice up your array in an efficient way:
public static IEnumerable<T> Slice(this T[,] values)
{
return Slice(values, 0, 0);
}
public static IEnumerable<T> Slice(this T[,] values, int index)
{
return Slice(values, 0, index);
}
public static IEnumerable<T> Slice(this T[,] values, int dimension, int index)
{
int length = values.GetUpperBound(dimension);
int[] point = new int[values.Rank];
point[dimension] = index;
dimension = 1 - dimension;// only works for rank == 2
for (int i = 0; i < length; i++)
{
point[dimension] = i;
yield return (T)values.GetValue(point);
}
}
It still needs some work because it only works with rank 2 arrays, but it should be fine for the example you posted.
Now you can call your calculate function like this:
Calculate(myArray.Slice(0));
Note that due to the way IEnumerable and the yield statement work the for loop in the code I posted is essentially free. It won't run until you actually iterate the items in your Calculate method, and even there runs in a "just-in-time" fashion so that the whole algorithm remains O(n).
It gets even more interesting when you share what your Calculate method is doing. You might be able to express it as a simple Aggregate + lambda expression. For example, let's say your calculate method returned the number of items > 5:
myArray.Slice(0).Count(x => x > 5);
Or say it summed all the items:
myArray.Slice().Sum();

A jagged array works the way you want:
double[][] jaggedArray = new double[][100];
for (int i = 0; i < jaggedArray.Length; ++i)
jaggedArray[i] = new double[100];
myFunction(jaggedArray[0]);
You can have different sizes for each array in this way.

A jagged array would let you split out the first array!

The Slice() method given above will get you a single row from your array, which seems to match the sample given in your question.
However, if you want a one dimensional array that contains all the elements in the rectangular array, you can use something like this, which is also O(n).
public static T[] Flatten<T>(this T[,] array)
where T : struct
{
int size = Marshal.SizeOf(array[0, 0]);
int totalSize = Buffer.ByteLength(array);
T[] result = new T[totalSize / size];
Buffer.BlockCopy(array, 0, result, 0, totalSize);
return result;
}

Related

Getting the number of elements in a generic array

For a school project, I need to return the number of elements in a generic array (a T[] array).
In the constructor I set the array like this:
T[] arr = new T[100];
arr.add(3);
arr.add(2);
arr.add(1);
To find the size of the array I tried array.length, however this return the capacity which would be 100 but it should be 3.
How could I find the correct answer 3?
EDIT:
The add function is a simple function that checks if the size is smaller than default_capacity add data to array. Size variable is crucial and the function expects the total number of elements in the array.
public void Add(T x)
{
if(size < DEFAULT_CAPACITY)
{
array[size] = x;
}
}
Here the array has not a capacity of 100: it has 100 items.
An array has no capacity, it has Length items...
For example, an array of 100 integers has 100 boxes initialized to 0.
And an array has no add method...
You may use a List<T> instead and you'll have Count property and Add method.
List<> is more smooth and usefull than arrays, but a little less optimized.
If the work is to use a such initialized array, you may use #itsme86 advice in question comment.
But here what is add method in your code?
You can use your intended array like that:
public class GenericArray<T>
{
public readonly T[] arr = new T[100];
}
var myArray = new GenericArray<int>();
myArray.arr[0] = 3;
myArray.arr[1] = 2;
myArray.arr[10] = 1;
And you still have 100 items: myArray.arr.Length is 100.
You can use a generic list like that:
public class GenericList<T>
{
public readonly List<T> list = new List<T>(100);
}
var myList = new GenericList<int>();
myList.list.Add(3);
myList.list.Add(2);
myList.list.Add(1);
And here you have 3 items: myList.list.Count is 3.
The list has here a capacity of 100: it means you can add items without resizing the internal array.

How do I use if/else in a generic function? C#

First I'd like to say that im pretty new in regards to coding, so don't kill me if the code looks horrible.
Alright, so the problem is that im trying to make a generic function which purpose is to take two sorted arrays and merge them into a new sorted array.
The problem im facing is trying to use if's in the function, and it doesn't let me use the < operand.
public object[] mergeTwoSorted<Tone, Ttwo>(Tone[] array, Ttwo[] array2)
{
object[] mergedArray = new object[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
}
}
}
Any idea on how to tackle this problem?
First I'd like to say that im pretty new in regards to coding, so don't kill me if the code looks horrible.
It looks pretty reasonable so far. It looks like what you're trying to do is to take two sorted arrays of two different types, but there is an ordering relationship between the two types. You're then merging the two arrays into an array of objects, such that the objects from the two arrays are still in the same order, but they are interleaved with each other to match the inter-type ordering relation, yes?
That is an unusual thing to do, but it is possible.
If that is not what you are doing, then you need to stop and re-design the code. In particular, if your intention is to take two arrays of the same type, then you must have one type parameter, not two, and you must make an array of T as the output, not object.
The problem is that there is in general no way to express "I have an ordering relationship between these two types" in C#. You'll have to provide a function that does that. Traditionally we provide a function that takes the two types and returns an integer: -1 if the first is the smaller, 1 if the second is the smaller, and 0 if they are equal.
If we have that then your method becomes:
public static object[] MergeTwoSorted<TOne, TTwo> (
TOne[] items1,
TTwo[] items2,
Func<TOne, TTwo, int> comparer)
{
Note that we are using standard C# conventions here. Methods begin with a capital, type parameters are TSomething, and so on. A Func<A, B, C> is a function that takes an A, a B, and returns a C. Methods that do not manipulate an instance of their class are static.
Note that there is no need to re-state in the name of a thing what its type is. Say what the thing is logically, not how it is stored:
object[] merged = new object[items1.Length + items2.Length];
Now your loop needs some work:
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
Arrays are never value types. I think you do not understand the difference between values and references, and that is really important to understand, so do some research on that.
Also, you have a counter i which counts the big merged array, but you use that as an index into the small arrays. That is very wrong; do you see why?
Think about it this way: you are going to count through both arrays at the same time filling in the merged array. So that would be:
int current1 = 0;
int current2 = 0;
while (current1 + current2 < merged.Length)
{
if (comparer(items1[current1], items2[current2]) < 0)
{
// items1[current1] is the smaller
merged[current1+current2] = items1[current1];
current1 += 1;
}
else
{
There is a bug in the code above; can you find it? Hint: indices for all array accesses must be >= 0 and < Length of the array. Is there a way in the code I've written so far that this gets violated?
Can you fix the bug and finish it off?
Can you now make a call site that takes an array of strings, an array of numbers, and an ordering relationship between strings and numbers, and merges the arrays?
public static object[] mergeTwoSorted<Tone, Ttwo>(Tone[] array, Ttwo[] array2)
where Tone : IComparable<Ttwo>
{
var mergedArray = new object[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i].CompareTo( array2[i] )>0)
}
}
return mergedArray;
}
If you want to use specific operations on Generics, you have to limit them with a where clause accordingly. As when working with Fractural Numebrs, you need a common denominator.
Something like a "IMergable" interface that you have to write yourself.
public IMergeable[] mergeTwoSorted<Tone, Ttwo> (Tone[] array, Ttwo[] array2)
where Tone : IMergeable, Ttwo : IMergeable
{
IMergeable[] mergedArray = new IMergeable[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
}
}
}
Alternatively, you could jsut use one type:
public Tone[] mergeTwoSorted<Tone> (Tone[] array, Tone[] array2)
{
IMergeable[] mergedArray = new IMergeable[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
}
}
}

Return null if array element does not exist

Is there a simple^ way of getting the value 'null' if an array element does not exist?
For example, in the code below sArray has 3 elements and the first 3 calls to SomeMethod work (prints true), however the 4th call SomeMethod(sArray[3]); gives me an IndexOutOfRangeException. Is there a way to make the 4th call to SomeMethod print false?
static void Main(string[] args)
{
int[] sArray = new int[]{1,2,3};
SomeMethod(sArray[0]);
SomeMethod(sArray[1]);
SomeMethod(sArray[2]);
SomeMethod(sArray[3]);
}
static void SomeMethod(int? s) => Console.WriteLine(s.HasValue);
^Would prefer single line expression
There is a Linq method ElementAtOrDefault
To use it the way you want to (returning null) you will need ti change the underlying type of your array to nullable int:
int?[] sArray = new int?[]{1,2,3};
SomeMethod(sArray.ElementAtOrDefault(1000));
How about an extension method?
public static T? TryGet<T>(this T[] source, int index) where T: struct
{
if (0 <= index && index < source.Length)
{
return source[index];
}
else
{
return null;
}
}
Then you could write:
static void Main(string[] args)
{
int[] sArray = new int[]{1,2,3};
SomeMethod(sArray.TryGet(0));
SomeMethod(sArray.TryGet(1));
SomeMethod(sArray.TryGet(2));
SomeMethod(sArray.TryGet(3));
}
SomeMethod(sArray.Skip(3).Select(z => (int?)z).FirstOrDefault());
is a working replacement of:
SomeMethod(sArray[3]);
The former will call SomeMethod with null (while the latter will throw an exception if the array doesn't have at least 4 entries).
In Skip(3) the 3 can be changed to whatever index you want to retrieve from the array. The Select is needed to project the int into a int? so that FirstOrDefault returns either the 4th element or null.
If you don't want to use LINQ then you could use:
SomeMethod(sArray.Length > 3 ? sArray[3] : (int?)null);
instead.
Or consider using:
foreach (var entry in sArray.Take(4))
{
SomeMethod(entry);
}
to loop through up to 4 elements of the array (it will work fine if there are fewer than 4 - it will just make fewer calls to SomeMethod).
Arrays in C# have a .Length property which you can check before trying to pass an item from one to SomeMethod, and the typical approach is to loop through each element of the array rather than guessing whether or not an index is valid:
for (int i = 0; i < sArray.Length; i++)
{
SomeMethod(sArray[i]);
}
You will not be able to avoid an IndexOutOfRangeException if you reference an index in an array that doesn't exist.
However, if you really want a method with this type of functionality, you could simply modify your existing code to check whether or not the index specified is greater than the length of the array.
Since your array is an int[] (and not an int?[]), all valid indexes will have a value. Also, we can use the ?. to handle cases where the array itself may be null:
private static void SomeMethod(int[] array, int index) =>
Console.WriteLine(index >= 0 && index < array?.Length);
Then in use, instead of passing an array item with an invalid index (which will always throw an IndexOutOfRangeException), you would pass the array itself and the index separately:
static void Main()
{
int[] sArray = new int[] { 1, 2, 3 };
SomeMethod(sArray, 0);
SomeMethod(sArray, 1);
SomeMethod(sArray, 2);
SomeMethod(sArray, 3);
SomeMethod(null, 0);
GetKeyFromUser("\nPress any key to exit...");
}
Output
in this case I'll suggest you to create a extension somewhere in your code like this
static class ArrExt
{
public static int? Get(this int[] arr, int i)
{
return (i >= 0 && i < arr.Length) ? arr[i] : default(int?);
}
}
then you can do this
int[] sArray = new int[] { 1, 2, 3 };
SomeMethod(sArray.Get(0));
SomeMethod(sArray.Get(1));
SomeMethod(sArray.Get(2));
SomeMethod(sArray.Get(3));
okay this is not a single line solution I know, but it's easier for both programmer and computer.

Is there any standard method to get array of objects from object with indexer by range of index keys?

As input i have object that implements IDataRecord(row of some abstract table), so it have indexer, and by giving it some integer i can retrive object of some type. As output my code must get some range of cells in that row as array of given type objects.
So I've written this method(yes, i know, it can be easly converted to extension method, but i don't need this, and also i don't really want to have this method visible outside of my class):
private static T[] GetRange<T>(IDataRecord row, int start, int length)
{
var result = new List<T>();
for (int i = start; i < (start + length); i++)
{
result.Add((T)row[i]);
}
return result.ToArray();
}
It works fine, but this method logic seems like something very common. So, is there any method that can give same(or almost same) result in .NET Framework FCL/BCL?
Use Skip and Take.
var rangeList = result.Skip(start - 1).Take(length);
No, it is not in the BCL.
You should however not create a List<> first and then copy that to the array. Either return the List<> itself (and construct it with the appropriate initial capacity), or create the array immediately like this:
private static T[] GetRange<T>(IDataRecord row, int start, int length)
{
var result = new T[length];
for (int i = 0; i < length; i++)
{
result[i] = (T)row[start + i];
}
return result;
}
Here is an alternative (for all you LINQ lovers):
// NB! Lazy enumeration
private static IEnumerable<T> GetRange<T>(IDataRecord row, int start, int length)
{
return Enumerable.Range(start, length).Select(i => (T)row[i]);
}
We repeat here what was stated in the comments to the question: The interface System.Data.IDataRecord (in System.Data.dll assembly) does not inherit IEnumerable<> or IEnumerable.
If you want to 'TakeARange' you should have a collection as input parameter.
Here you don't have one.
You just have a IDataRecord (eg. a single row) that has an indexer.
You should expose a property called Cells that return the list you work with in the indexer implementation.
Your method should look like this:
private static T[] TakeRange<T>(IEnumerable cells, int start, int length)
{
return cells.Skip(start - 1).Take(length)
}
Well.. Seem's like there is no such method in FCL/BCL

whats up with this foreach in c#?

(edit: Slight tidy of the code.)
Using foreach like this works fine.
var a = new List<Vector2>();
a.ForEach(delegate(Vector2 b) {
b.Normalize(); });
The following however causes "No overload for method 'ForEach' takes 1 arguments".
byte[,,] a = new byte[2, 10, 10];
a.ForEach(delegate(byte b) {
b = 1; });
I would recommend you just use a normal foreach loop to transform the data. You're using a method that exists only on the List<T> implementation, but not on arrays.
Using a method for foreach really gains you nothing, unless for some reason you were wanting to do data mutation in a method chain. In that case, you may as well write your own extension method for IEnumerable<T>. I would recommend against that, though.
Having a separate foreach loop makes it clear to the reader that data mutation is occurring. It also removes the overhead of calling a delegate for each iteration of the loop. It will also work regardless of the collection type as long as it is an IEnumerable (not entirely true, you can write your own enumerators and enumerables, but that's a different question).
If you're looking to just do data transformations (i.e. projections and the like) then use LINQ.
Also keep in mind that with the array, you're getting a copy of the byte not a reference. You'll be modifying just that byte not the original. Here's an example with the output:
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
Array.ForEach(numbers, number => number += 1);
foreach(int number in numbers)
{
Console.WriteLine(number);
}
Which yields the output:
1
2
3
4
5
As you can see, the number += 1 in the lambda had no effect. In fact, if you tried this in a normal foreach loop, you would get a compiler error.
You're using two different ForEach'es.
Array.ForEach in the byte[,,] example (though you're using it incorrectly) and List.ForEach in the List<...> example.
You've used syntax of List.ForEach() method for the array, but Array.ForEach() syntax is:
public static void ForEach<T>(
T[] array,
Action<T> action
)
One important point that array should be one-dimensional in order to use it in Array.ForEach(). Considering this I would suggest using simple for loop
// first dimension
for (int index = 0; index < bytesArray.GetLength(0); index++)
// second dimension
for (int index = 0; index < bytesArray.GetLength(1); index++)
// third dimension
for (int index = 0; index < bytesArray.GetLength(2); index++)
I don't know of a ForEach method that takes multi-dimensional arrays.
If you want one, i think you will have to create it yourself.
Here is how to do it:
private static void ForEach<T>(T[, ,] a, Action<T> action)
{
foreach (var item in a)
{
action(item);
}
}
Sample program, using the new ForEach method:
static class Program
{
static void Main()
{
byte[, ,] a = new byte[2, 10, 10];
ForEach(a, delegate(byte b)
{
Console.WriteLine(b);
});
}
private static void ForEach<T>(T[, ,] a, Action<T> action)
{
foreach (var item in a)
{
action(item);
}
}
}
Also, the ForEach method in the Array version, is not an instance method, it is statis method. You call it like this:
Array.ForEach(array, delegate);
raw arrays have much less instance methods than generic collections because they are not templated. These methods, such as ForEach() or Sort() are usually implemented as static methods which are themselves templated.
In this case, Array.Foreach(a, action) will do the trick for the array.
Of course, the classical foreach(var b in a) would work for both List and Array since it only requires an enumerator.
However:
I'm not sure how you'd loop over a multidimensional array.
Your assignment (b=1) won't work. Because you receive a value, not a reference.
List has the first instance method. Arrays do not.

Categories