Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have got a "project" where I am only allowed to use arrays. In this project, I have to read data from a txt file then for example find the second-best lap time among runners and etc.
I can always calculate the correct answer, but I am not sure about my array management.
As you know, in C# you have to provide the length of the array at initialization. Since there are tasks where I don't know the size of the array what I did was declare a far bigger array then use this function:
private static string[] ArraySimplyfier(string[] strs, int index)
{
string[] returnStr = new string[index];
for (int i = 0; i < returnStr.Length; i++)
{
returnStr[i] = strs[i];
}
return returnStr;
}
As you can see it "clears" the nulls from the array, but the thing is I have to create it for every type of array which is not ideal. Do you have any other ideas for my problem?
Creating new, bigger arrays and copying is the way to go. You are on the right track there. This is basically what List is doing under the hood (in an optimized way).
If you need this for multiple types, you could look into generic methods. Generics allow you to specify classes and methods for multiple types.
A List will double in size when it gets too small, you can do the same. Check the "Add/EnsureCapacity" implementation for List on github.
// Adds the given object to the end of this list. The size of the list is
// increased by one. If required, the capacity of the list is doubled
// before adding the new element.
//
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
// Ensures that the capacity of this list is at least the given minimum
// value. If the currect capacity of the list is less than min, the
// capacity is increased to twice the current capacity or to min,
// whichever is larger.
private void EnsureCapacity(int min) {
if (_items.Length < min) {
int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;
// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
if (newCapacity < min) newCapacity = min;
Capacity = newCapacity;
}
}
So your question seems to be "when filtering an array, how do I know the number of elements on beforehand, to get the size of the target array?" - you don't.
Either loop twice over the source array, once to get the count and then do the copy, or start with an equally sized array, keep the count and truncate the array by copying it to a new one with the size capped to the number of results.
You can use generics. That way u have one method for every array type.
Your method would look like this:
private static T[] ArraySimplyfier<T>(T[] array, int index)
{
T[] result = new T[index];
for (int i = 0; i < result.Length; i++)
{
result[i] = array[i];
}
return result;
}
and u can call this method like this:
//example for a string array
ArraySimplyfier<string>(new string[45], 17); //replace 'new string[45]' with your array
Related
I am new to C#. The following code was a solution I came up to solve a challenge. I am unsure how to do this without using List since my understanding is that you can't push to an array in C# since they are of fixed size.
Is my understanding of what I said so far correct?
Is there a way to do this that doesn't involve creating a new array every time I need to add to an array? If there is no other way, how would I create a new array when the size of the array is unknown before my loop begins?
Return a sorted array of all non-negative numbers less than the given n which are divisible both by 3 and 4. For n = 30, the output should be
threeAndFour(n) = [0, 12, 24].
int[] threeAndFour(int n) {
List<int> l = new List<int>(){ 0 };
for (int i = 12; i < n; ++i)
if (i % 12 == 0)
l.Add(i);
return l.ToArray();
}
EDIT: I have since refactored this code to be..
int[] threeAndFour(int n) {
List<int> l = new List<int>(){ 0 };
for (int i = 12; i < n; i += 12)
l.Add(i);
return l.ToArray();
}
A. Lists is OK
If you want to use a for to find out the numbers, then List is the appropriate data structure for collecting the numbers as you discover them.
B. Use more maths
static int[] threeAndFour(int n) {
var a = new int[(n / 12) + 1];
for (int i = 12; i < n; i += 12) a[i/12] = i;
return a;
}
C. Generator pattern with IEnumerable<int>
I know that this doesn't return an array, but it does avoid a list.
static IEnumerable<int> threeAndFour(int n) {
yield return 0;
for (int i = 12; i < n; i += 12)
yield return i;
}
D. Twist and turn to avoid a list
The code could for twice. First to figure the size or the array, and then to fill it.
int[] threeAndFour(int n) {
// Version: A list is really undesirable, arrays are great.
int size = 1;
for (int i = 12; i < n; i += 12)
size++;
var a = new int[size];
a[0] = 0;
int counter = 1;
for (int i = 12; i < n; i += 12) a[counter++] = i;
}
if (i % 12 == 0)
So you have figured out that the numbers which divides both 3 and 4 are precisely those numbers that divides 12.
Can you figure out how many such numbers there are below a given n? - Can you do so without counting the numbers - if so there is no need for a dynamically growing container, you can just initialize the container to the correct size.
Once you have your array just keep track of the next index to fill.
You could use Linq and Enumerable.Range method for the purpose. For example,
int[] threeAndFour(int n)
{
return Enumerable.Range(0,n).Where(x=>x%12==0).ToArray();
}
Enumerable.Range generates a sequence of integral numbers within a specified range, which is then filtered on the condition (x%12==0) to retrieve the desired result.
Since you know this goes in steps of 12 and you know how many there are before you start, you can do:
Enumerable.Range(0,n/12+1).Select(x => x*12).ToArray();
I am unsure how to do this without using List since my understanding is that you can't push to an array in C# since they are of fixed size.
It is correct that arrays can not grow. List were invented as a wrapper around a array that automagically grows whenever needed. Note that you can give List a integer via the Constructor, wich will tell it the minimum size it should expect. It will allocate at least that much the first time. This can limit growth related overhead.
And dictionaries are just a variation of the list mechanics, with Hash Table key search speed.
There is only 1 other Collection I know of that can grow. However it is rarely mentioned outside of theory and some very specific cases:
Linked Lists. The linked list has a unbeatable growth performance and the lowest issue of running into OutOfMemory Exceptions due to Fragmentation. Unfortunately, their random access times are the worst as a result. Unless you can process those collections exclusively sequentally from the start (or sometimes the end), their performance will be abysmal. Only stacks and queues are likely to use them. There is however still a implementation you could use in .NET: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1
Your code holds some potential too:
for (int i = 12; i < n; ++i)
if (i % 12 == 0)
l.Add(i);
It would way more effective to count up by 12 every itteration - you are only interested in every 12th number after all. You may have to change the loop, but I think a do...while would do. Also the array/minimum List size is easily predicted: Just divide n by 12 and add 1. But I asume that is mostly mock-up code and it is not actually that deterministic.
List generally works pretty well, as I understand your question you have challenged yourself to solve a problem without using the List class. An array (or List) uses a contiguous block of memory to store elements. Arrays are of fixed size. List will dynamically expand to accept new elements but still keeps everything in a single block of memory.
You can use a linked list https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1?view=netframework-4.8 to produce a simulation of an array. A linked list allocates additional memory for each element (node) that is used to point to the next (and possibly the previous). This allows you to add elements without large block allocations, but you pay a space cost (increased use of memory) for each element added. The other problem with linked lists are you can't quickly access random elements. To get to element 5, you have to go through elements 0 through 4. There's a reason arrays and array like structures are favored for many tasks, but it's always interesting to try to do common things in a different way.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Hi I need to work out the average of 10 integers stored in a database.
This is what I've got so far.
private void getAverage()
{
StudentsDataSet.StudentsRow courseMarks = studentsDataSet.Students.Course[10];
average = 0;
int[] array;
for (int index = 0; index < 10 ; index++)
{
average += array[index];
}
average = average / 10;
averageBox.Text = average.ToString();
}
As you can see I have no idea what I'm doing... any pointers??
There are a couple of issues with your code, which I will address one by one:
You are always getting the data from row 11 of your data table (studentsDataSet.Students.Course[10]). That's probably not what you want.
You are declaring your array but you are not initializing it.
You need to fill the array before you loop over it.
You are repeating the number of courses (10) in many places in your code (e.g. the for loop and the divisor of the average). This will be a source of errors when the number of courses changes.
Be careful to choose good names for your variables. average should be sum (that's what it actually stores) and array is a poor choice, because it does not describe the content of the variable, but its format.
According to the C# naming convention methods are in PascalCase.
Here is some code that will do the job. I have added comments to explain exactly what is happening:
//The method that will add the average to the text box. You need to provide the row index.
//One common way to get the row index would be the SelectedIndex of your data grid
private void GetAverage(int rowIndex)
{
//Get the row whose data you want to process
DataRow courseMarks = studentsDataSet.Students.Rows[rowIndex];
//Initialize an array with the names of the source columns
string[] courseColumnNames = { "Course1", "Course2", "Course3", "Course4", "Course5",
"Course6", "Course7", "Course8", "Course9", "Course10" };
//Declare and initialize the array for the marks
int[] markValues = new int[courseColumnNames.Length];
//Fill the array with data
for (int index = 0; index < courseMarks.Length ; index++)
{
//Get the column name of the current course
string columnName = courseColumnNames[index];
//Copy the column value into the array
markValues[index] = (int)courseMarks[columnName];
}
//Calculate the sum
int sum = 0;
for (int index = 0; index < courseMarks.Length ; index++)
{
sum += markValues[index];
}
//Calculate the average from the sum
//Note: Since it's an integer division there will be no decimals
int average = sum / courseMarks.Length;
//Display the data
averageBox.Text = average.ToString();
}
Note that of course you could merge the two loops into one (and even drop the array, since you could just directly add to the sum variable). I have not done that to keep every step of the process clearly separated. You could also use the LINQ method Average, but that would quite miss the point here.
Instead of all your codes, you may try this:
private double GetAverage()
{
StudentsDataSet.StudentsRow courseMarks = studentsDataSet.Students.Course[10];
var average=courseMarks.Average(x =>(double) x["*YourDataColumnName*"]);
return average;
}
Where YourDataColumnName is the column of your row which contains the value you want to get average of that
You could do the following :
int average = studentsDataSet.Students.Course.Average();
You would have to add using System.Linq; for this to work.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I've created my own list with a static int as length. How are these things done when you don't know the size of a collection and you want to construct it. I know there is a build in list but I want to build my own to understand the inner working of it. I defined it as size = int 5 in the constructor so it will output now 1 2 3 0 0 and I want to know how to resize it and using a constructor with undefined length. I can't figure it out myself some help is appreciated.
I fixed it. Thanks for the answers guys really fast and easy to understand I never heard from the .net reference so thanks for the site.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List l = new List();
l.Add(1);
l.Add(2);
l.Add(3);
l.Add(4);
foreach (int n in l)
{
Console.WriteLine(n);
}
Console.Read();
}
}
class List
{
private int _lLength;
private int[] _lArray;
private int _lPos;
public List()
{
/*
* Create an array with a default size
* If it doesn't fit anymore create a new one and copy it
* to a new array and double the size
*/
this._lArray = new int[2];
}
public List(int c)
{
this._lLength = c;
this._lArray = new int[this._lLength];
this._lPos = 0;
}
public void Add(int n)
{
if (this._lArray.Length <= this._lPos)
{
// So now is the array < then the array we want to return
int[] tmp = this._lArray;
this._lArray = new int[tmp.Length * 2];
Array.Copy(tmp, this._lArray, tmp.Length);
}
this._lArray[this._lPos++] = n;
}
public IEnumerator<int> GetEnumerator()
{
foreach (int n in this._lArray)
yield return n;
}
}
}
Internally, the List<T> object keeps an array with a default size (0, according to the reference source). When the array is full, a new array is created, double size of the previous one, and all items from the first array are moved to the new array.
So adding an item to this list (array size = 2):
item 1
item 2
Causes the array behind the list to become (array size = 4):
item 1
item 2
item 3
null
If you know the probable size of the list on beforehand, you could opt to pass the expected number to the constructor of List<T>. The array size will be set to that length, which may give you better performance overall, since it doesn't have to recreate arrays.
I have an Array variable. I can use the Rank property to get the number of dimensions and I know that you can use a foreach to visit each element as if the array was flattened. However, I wish to modify elements and change element references. I cannot dynamically create the correct number of for loops and I cannot invalidate an enumerator.
EDIT
Thanks for the comments, sorry about the previous lack of clarity at the end of a long tiring day. The problem:
private void SetMultiDimensionalArray(Array array)
{
for (int dimension = 0; dimension < array.Rank; dimension++)
{
var len = array.GetLength(dimension);
for (int k = 0; k < len; k++)
{
//TODO: do something to get/set values
}
}
}
Array array = new string[4, 5, 6];
SetMultiDimensionalArray(array);
Array array = new string[2, 3];
SetMultiDimensionalArray(array);
I had another look before reading this page and it appears all I need to do is create a list of integer arrays and use the overloads of GetValue and SetValue -
Array.GetValue(params int[] indices)
Array.SetValue(object value, params int[] indices)
Everything seems clear now unless someone can suggest a superior method. svick has linked to this so I will accept this answer barring any further suggestions.
It's hard to tell what exactly do you need, because your question is quite unclear.
But if you have a multidimensional array (not jagged array) whose rank you know only at runtime, you can use GetValue() to get the value at specified indices (given as an array of ints) and SetValue() to set it.
I'm searching the way(s) to fill an array with numbers from 0 to a random. For example, from 0 to 12 or 1999, etc.
Of course, there is a for-loop:
var arr = int[n];
for(int i = 0; i < n; i++)
{
arr[i] = i;
}
And I can make this method been an extension for Array class. But is there some more interesting ways?
This already exists(returns IEnumerable, but that is easy enough to change if you need):
arr = Enumerable.Range(0, n);
The most interesting way in my mind produces not an array, but an IEnumerable<int> that enumerates the same number - it has the benefit of O(1) setup time since it defers the actual loop's execution:
public IEnumerable<int> GetNumbers(int max) {
for (int i = 0; i < max; i++)
yield return i;
}
This loop goes through all numbers from 0 to max-1, returning them one at a time - but it only goes through the loop when you actually need it.
You can also use this as GetNumbers(max).ToArray() to get a 'normal' array.
The best answer depends on why you need the array. The thing is, the value of any array element is equal to the index, so accessing any element is essentially a redundant operation. Why not use a class with an indexer, that just returnes the value of the index? It would be indistinguishable from a real array and would scale to any size, except it would take no memory and no time to set up. But I get the feeling it's not speed and compactness you are after. Maybe if you expand on the problem, then a better solution will be more obvious.