Checking the values in an array around a specific index - c#

I have a 2 dimensional array and what I want to do is search for a specific value around that index(so I want to check array[x-1,y], array[x,y-1] and so on).
My problem is when it will check index which is out of range. Is there someway to check them (I don't want to a lot of IF's checking if x-1 or y-1 is in range of course). I haven't used try/catch a lot yet, and I'm not sure how that works either, but can I ignore the out of range Exception with that? Or is there a better solution for this problem?

If you can't have if you can pre-compute lists of indexes for each cell and iterate over them. I.e. have separate array of the same dimensions that contains lists of index pairs for iteration. Inner elements would have 8 pairs each, corner and border elements would have less (3 and 5 correspondingly).
Alternatively you can use ? : instead of if condition in case restriction is only on syntax and not on condition itself.

I suggest using an extension method for hiding the logic in it (weather with if's or with Math.Max and Math.Min):
public static partial class Array2DExtensions {
public static IEnumerable<T> Vicinity<T>(this T[,] data, int line, int col) {
if (null == data)
throw new ArgumentNullException("data");
//TODO: you may want to add range check here
for (int i = Math.Max(data.GetLowerBound(0), line - 1);
i <= Math.Min(data.GetUpperBound(0), line + 1);
++i)
for (int j = Math.Max(data.GetLowerBound(1), col - 1);
j <= Math.Min(data.GetUpperBound(1), col + 1);
++j)
yield return data[i, j];
}
}
And so you can put something like this:
int[,] sample = ...
...
// Are there any value less than 100 in vicinity of 5, 7 item?
bool found = sample
.Vicinity(5, 7)
.Any(item => item < 100);

You can calculate the safe lower and upper limits for both dimensions and then iterate over the matrix:
// Calculate x range to check
var xl = Math.Max(x-1, 0);
var xu = Math.Min(x+1, array.GetUpperBound(1));
// Calculate y range to check
var yl = Math.Max(y-1, 0);
var yu = Math.Min(y+1, array.GetUpperBound(0));
// Iterate using ranges
for (var j=yl; j <= yu; j++)
for (var i=xl; i <= xu; i++)
// Do the checking
// array[j, i]

I think you'll have to check for each index if it around the array edges.
with the necessary "if's"
if you want to use try and catch it will work,
but you'll have to surround each array access with it's own Try And Catch
(if all will be in same try, once exception was caught the following commands will be skipped)
like this:
try
{
array[x-1,y]
}
catch (ArgumentOutOfRangeException ex)
{
}
try
{
array[x,y-1]
}
catch (ArgumentOutOfRangeException ex)
{
}
etc..

Related

Range in for loop using Array

I'm pretty new to C# and want the users to be able to write in 5 numbers between 1 to 25. The issue I'm having is that I don't want the user to type a number over 25 or a number below 1.
Also this is a task for my studies and my teacher want us to use arrays so I'm not allowed to use List.
int[] usernum = new int[4];
for (int i = 0; i < usernum.Length; i++)
{
usernum[i] = Convert.ToInt32(Console.ReadLine());
}
Ok, to start off, some annotations to your code:
int[] usernum = new int[4]; // should be: new int[5];
for (int i = 0; i < usernum.Length; i++)
{
usernum[i] = Convert.ToInt32(Console.ReadLine()); // use int.TryParse instead
}
Now, I don't want to just give you the code, since this should obviously be a learning experience.
What you need to do, though is integrate a "validation" cycle. That means:
Read in string from user
Try to parse string to number
If that fails: back to 1.
Check if number < 1 or > 25
If so: back to 1.
If you are here, you passed both checks and can
set usernum[i] = number
Next "i"
Obviously, there are some slight variations in how you twist and turn your checks and arrange loops which are equally valid.
For example: You can decide if you want to check if number is inside bounds or if you want to check if the number is outside bounds and jump or not jump accordingly ...
Why int.TryParse instead of Convert.ToInt32?
There are some rule of thumbs that can spare you from severe headaches:
"Never trust user input"
"Do not use exceptions for control flow"
Using Convert here, breaks both.
For one, Convert.ToInt32 throws if the string does not represent an integer value (chars other than +-0..9, value > int.Max or < int.Min). So in using it, you trust the user to type in a valid integer. Not a good idea.
Then, it throwing means: the case, that a user (maybe just made a typo) did not provide valid input is controlling your flow to error handling. But this case is not at all "exceptional". In fact, you should expect it. int.TryParse makes this possible, in that it returns you a flag (boolean) that informs you about success or failure of the conversion attempt (instead of throwing).
Though I would recommend you to learn if else loop first https://www.w3schools.com/cs/cs_conditions.asp
here is the code if needed
int[] usernum = new int[4];
for (int i = 0; i < usernum.Length; i++)
{
var result = Console.ReadLine();
int currentResult;
if (!int.TryParse(result, out currentResult))
{
Console.WriteLine("Invalid input - must be a valid integer value");
i--;
continue;
}
if(currentResult < 1 || currentResult > 25)
{
Console.WriteLine("Invalid input - must be between 1 & 25");
i--;
continue;
}
usernum[i] = currentResult;
}
for-loop might not be the ideal solution for this use-case where you need to conditionally increment the index.
This should do the trick:
int[] userNumbers = new int[5];
int i = 0;
while (i < userNumbers.Length)
{
string rawInput = Console.ReadLine();
bool isNumberValid = int.TryParse(rawInput, out int inputNumber); // as suggested by #Fildor
if(isNumberValid && inputNumber >= 1 && inputNumber <= 25) // increment counter only if 1 <= input <= 25
{
userNumbers[i] = inputNumber;
i++;
}
}

How to Order By or Sort an integer List and select the Nth element

I have a list, and I want to select the fifth highest element from it:
List<int> list = new List<int>();
list.Add(2);
list.Add(18);
list.Add(21);
list.Add(10);
list.Add(20);
list.Add(80);
list.Add(23);
list.Add(81);
list.Add(27);
list.Add(85);
But OrderbyDescending is not working for this int list...
int fifth = list.OrderByDescending(x => x).Skip(4).First();
Depending on the severity of the list not having more than 5 elements you have 2 options.
If the list never should be over 5 i would catch it as an exception:
int fifth;
try
{
fifth = list.OrderByDescending(x => x).ElementAt(4);
}
catch (ArgumentOutOfRangeException)
{
//Handle the exception
}
If you expect that it will be less than 5 elements then you could leave it as default and check it for that.
int fifth = list.OrderByDescending(x => x).ElementAtOrDefault(4);
if (fifth == 0)
{
//handle default
}
This is still some what flawed because you could end up having the fifth element being 0. This can be solved by typecasting the list into a list of nullable ints at before the linq:
var newList = list.Select(i => (int?)i).ToList();
int? fifth = newList.OrderByDescending(x => x).ElementAtOrDefault(4);
if (fifth == null)
{
//handle default
}
Without LINQ expressions:
int result;
if(list != null && list.Count >= 5)
{
list.Sort();
result = list[list.Count - 5];
}
else // define behavior when list is null OR has less than 5 elements
This has a better performance compared to LINQ expressions, although the LINQ solutions presented in my second answer are comfortable and reliable.
In case you need extreme performance for a huge List of integers, I'd recommend a more specialized algorithm, like in Matthew Watson's answer.
Attention: The List gets modified when the Sort() method is called. If you don't want that, you must work with a copy of your list, like this:
List<int> copy = new List<int>(original);
List<int> copy = original.ToList();
The easiest way to do this is to just sort the data and take N items from the front. This is the recommended way for small data sets - anything more complicated is just not worth it otherwise.
However, for large data sets it can be a lot quicker to do what's known as a Partial Sort.
There are two main ways to do this: Use a heap, or use a specialised quicksort.
The article I linked describes how to use a heap. I shall present a partial sort below:
public static IList<T> PartialSort<T>(IList<T> data, int k) where T : IComparable<T>
{
int start = 0;
int end = data.Count - 1;
while (end > start)
{
var index = partition(data, start, end);
var rank = index + 1;
if (rank >= k)
{
end = index - 1;
}
else if ((index - start) > (end - index))
{
quickSort(data, index + 1, end);
end = index - 1;
}
else
{
quickSort(data, start, index - 1);
start = index + 1;
}
}
return data;
}
static int partition<T>(IList<T> lst, int start, int end) where T : IComparable<T>
{
T x = lst[start];
int i = start;
for (int j = start + 1; j <= end; j++)
{
if (lst[j].CompareTo(x) < 0) // Or "> 0" to reverse sort order.
{
i = i + 1;
swap(lst, i, j);
}
}
swap(lst, start, i);
return i;
}
static void swap<T>(IList<T> lst, int p, int q)
{
T temp = lst[p];
lst[p] = lst[q];
lst[q] = temp;
}
static void quickSort<T>(IList<T> lst, int start, int end) where T : IComparable<T>
{
if (start >= end)
return;
int index = partition(lst, start, end);
quickSort(lst, start, index - 1);
quickSort(lst, index + 1, end);
}
Then to access the 5th largest element in a list you could do this:
PartialSort(list, 5);
Console.WriteLine(list[4]);
For large data sets, a partial sort can be significantly faster than a full sort.
Addendum
See here for another (probably better) solution that uses a QuickSelect algorithm.
This LINQ approach retrieves the 5th biggest element OR throws an exception WHEN the list is null or contains less than 5 elements:
int fifth = list?.Count >= 5 ?
list.OrderByDescending(x => x).Take(5).Last() :
throw new Exception("list is null OR has not enough elements");
This one retrieves the 5th biggest element OR null WHEN the list is null or contains less than 5 elements:
int? fifth = list?.Count >= 5 ?
list.OrderByDescending(x => x).Take(5).Last() :
default(int?);
if(fifth == null) // define behavior
This one retrieves the 5th biggest element OR the smallest element WHEN the list contains less than 5 elements:
if(list == null || list.Count <= 0)
throw new Exception("Unable to retrieve Nth biggest element");
int fifth = list.OrderByDescending(x => x).Take(5).Last();
All these solutions are reliable, they should NEVER throw "unexpected" exceptions.
PS: I'm using .NET 4.7 in this answer.
Here there is a C# implementation of the QuickSelect algorithm to select the nth element in an unordered IList<>.
You have to put all the code contained in that page in a static class, like:
public static class QuickHelpers
{
// Put the code here
}
Given that "library" (in truth a big fat block of code), then you can:
int resA = list.QuickSelect(2, (x, y) => Comparer<int>.Default.Compare(y, x));
int resB = list.QuickSelect(list.Count - 1 - 2);
Now... Normally the QuickSelect would select the nth lowest element. We reverse it in two ways:
For resA we create a reverse comparer based on the default int comparer. We do this by reversing the parameters of the Compare method. Note that the index is 0 based. So there is a 0th, 1th, 2th and so on.
For resB we use the fact that the 0th element is the list-1 th element in the reverse order. So we count from the back. The highest element would be the list.Count - 1 in an ordered list, the next one list.Count - 1 - 1, then list.Count - 1 - 2 and so on
Theorically using Quicksort should be better than ordering the list and then picking the nth element, because ordering a list is on average a O(NlogN) operation and picking the nth element is then a O(1) operation, so the composite is O(NlogN) operation, while QuickSelect is on average a O(N) operation. Clearly there is a but. The O notation doesn't show the k factor... So a O(k1 * NlogN) with a small k1 could be better than a O(k2 * N) with a big k2. Only multiple real life benchmarks can tell us (you) what is better, and it depends on the size of the collection.
A small note about the algorithm:
As with quicksort, quickselect is generally implemented as an in-place algorithm, and beyond selecting the k'th element, it also partially sorts the data. See selection algorithm for further discussion of the connection with sorting.
So it modifies the ordering of the original list.

Dice Sorensen Distance error calculating Bigrams without using Intersect method

I have been programming an object to calculate the DiceSorensen Distance between two strings. The logic of the operation is not so difficult. You calculate how many two letter pairs exist in a string, compare it with a second string and then perform this equation
2(x intersect y)/ (|x| . |y|)
where |x| and |y| is the number of bigram elements in x & y. Reference can be found here for further clarity https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient
So I have tried looking up how to do the code online in various spots but every method I have come across uses the 'Intersect' method between two lists and as far as I am aware this won't work because if you have a string where the bigram already exists it won't add another one. For example if I had a string
'aaaa'
I would like there to be 3 'aa' bigrams but the Intersect method will only produce one, if i am incorrect on this assumption please tell me cause i wondered why so many people used the intersect method. My assumption is based on the MSDN website https://msdn.microsoft.com/en-us/library/bb460136(v=vs.90).aspx
So here is the code I have made
public static double SorensenDiceDistance(this string source, string target)
{
// formula 2|X intersection Y|
// --------------------
// |X| + |Y|
//create variables needed
List<string> bigrams_source = new List<string>();
List<string> bigrams_target = new List<string>();
int source_length;
int target_length;
double intersect_count = 0;
double result = 0;
Console.WriteLine("DEBUG: string length source is " + source.Length);
//base case
if (source.Length == 0 || target.Length == 0)
{
return 0;
}
//extract bigrams from string 1
bigrams_source = source.ListBiGrams();
//extract bigrams from string 2
bigrams_target = target.ListBiGrams();
source_length = bigrams_source.Count();
target_length = bigrams_target.Count();
Console.WriteLine("DEBUG: bigram counts are source: " + source_length + " . target length : " + target_length);
//now we have two sets of bigrams compare them in a non distinct loop
for (int i = 0; i < bigrams_source.Count(); i++)
{
for (int y = 0; y < bigrams_target.Count(); y++)
{
if (bigrams_source.ElementAt(i) == bigrams_target.ElementAt(y))
{
intersect_count++;
//Console.WriteLine("intersect count is :" + intersect_count);
}
}
}
Console.WriteLine("intersect line value : " + intersect_count);
result = (2 * intersect_count) / (source_length + target_length);
if (result < 0)
{
result = Math.Abs(result);
}
return result;
}
In the code somewhere you can see I call a method called listBiGrams and this is how it looks
public static List<string> ListBiGrams(this string source)
{
return ListNGrams(source, 2);
}
public static List<string> ListTriGrams(this string source)
{
return ListNGrams(source, 3);
}
public static List<string> ListNGrams(this string source, int n)
{
List<string> nGrams = new List<string>();
if (n > source.Length)
{
return null;
}
else if (n == source.Length)
{
nGrams.Add(source);
return nGrams;
}
else
{
for (int i = 0; i < source.Length - n; i++)
{
nGrams.Add(source.Substring(i, n));
}
return nGrams;
}
}
So my understanding of the code step by step is
1) pass in strings
2) 0 length check
3) create list and pass up bigrams into them
4) get the lengths of each bigram list
5) nested loop to check in source position[i] against every bigram in target string and then increment i until no more source list to check against
6) perform equation mentioned above taken from wikipedia
7) if result is negative Math.Abs it to return a positive result (however i know the result should be between 0 and 1 already this is what keyed me into knowing i was doing something wrong)
the source string i used is source = "this is not a correct string" and the target string was, target = "this is a correct string"
the result I got was -0.090909090908
I'm SURE (99%) that what I'm missing is something small like a mis-calculated length somewhere or a count mis-count. If anyone could point out what i'm doing wrong I'd be really grateful. Thank you for your time!
This looks like homework, yet this similarity metric on strings is new to me so I took a look.
Algorith implementation in various languages
As you may notice the C# version uses HashSet and takes advantage of the IntersectWith method.
A set is a collection that contains no duplicate elements, and whose
elements are in no particular order.
This solves your string 'aaaa' puzzle. Only one bigram there.
My naive implementation on Rextester
If you prefer Linq then I'd suggest Enumerable.Distinct, Enumerable.Union and Enumerable.Intersect. These should mimic very well the duplicate removal capabilities of the HashSet.
Also found this nice StringMetric framework written in Scala.

Argument out of range exception

Can anyone tell me why I am getting this exception? Well I know "why" I am getting it is cause it says that the element in the list I am trying to access does not exist, but stepping through the code I can see that it actually does exist?
In Game1 I call a helper class' calculation method. Then when the calculation is done, I add the result to a List<> in the CalculationHelper class.
The code for this looks like:
In Game1 class where I have instanciated CalculationHelper calcHelper class and then call one of the class methods.
Fisrt, in Game1 I call calcHelper.ForceVanishingPointIntersections(...) method in a for() loop which can be seen below. This works just fine and the calcHelper.ForceVanishingPointIntersections(...) method adds the value to the IntersectionPointList<> in the CalculationHelper class (see method below).
for (int i = 0; i < LineList.Count; i++)
{
calcHelper.ForceVanishingPointIntersections(LineList[i], LineList[i + 1]);
AddSprite(VanishingPointIntersectionList, calcHelper.GetIntersectionPointListElementAt(i), greenCirlce);
i++;
}
To do some calculations and add a value to the IntersectionPointlist in CalculationHelper class I do:
List<Vector2> IntersectionPointList = new List<Vector2>();
public void ForceVanishingPointIntersections(Line line1, Line line2)
{
Vector2 intersectionPoint;
// Do some calculations
IntersectionPointList.Add(intersectionPoint);
}
Finally in Game1 class, the second method in the for() loop I call AddSprite to create a Sprite. I want to pass in the values stored in CalculationHelper class IntersectionPointList as coordinates for the Sprite.
To this end I call calcHelper.GetIntersectionPointListElementAt(i) which calls a method in CalculationHelper class like so (which should return the value at the specified point (i) from the IntersectionPointList<>)
public Vector2 GetIntersectionPointListElementAt(int index)
{
Vector2 result = this.IntersectionPointList[index];
return result;
}
The first time the for() loop executes, this works fine. I do the calculations, the value is stored in the list and I am able to get this value from the list and pass it to AddSprite(..). However, the second time round the for() loop, when I call GetIntersectionPointListElementAt from the AddSprite() method, I get an exception in my
public Vector2 GetIntersectionPointListElementAt(int index)
{
Vector2 result = this.IntersectionPointList[index]; // Exception thrown here
return result;
}
saying the index was out of range? But stepping through the code, my IntersectionPointList is updated and it shows that the list now contains 2 values. And I'm trying to access the second value.
Does anyone have an idea why this could be?
For the life of me I cant figure out where I am going wrong.
If more code is needed I can post some more, just let me know
Because you access LineList[] with an index i + 1 you must diminish the last index by one in the for-condition. (Note the -1)
for (int i = 0; i < LineList.Count - 1; i++) {
calcHelper.ForceVanishingPointIntersections(LineList[i], LineList[i + 1]);
AddSprite( ... );
}
This will call ForceVanishingPointIntersections with the indexes
(0, 1), (1, 2), (2, 3), ... (Count-2, Count-1)
Note that the index range of LineList is 0 ... Count-1.
If non-overlapping indexes are required instead, like
(0, 1), (2, 3), (4, 5), ... (Count-2, Count-1)
then change the loop to
for (int i = 0; i < LineList.Count - 1; i += 2) {
calcHelper.ForceVanishingPointIntersections(LineList[i], LineList[i + 1]);
AddSprite(
VanishingPointIntersectionList,
calcHelper.GetIntersectionPointListElementAt(i / 2),
greenCirlce);
}
EDIT According the Chris Gessler's comments this second approach is the right one.
Remove the i++ inside the loop, it's quite uncommon and confusing. Instead, replace it by i += 2 in loop header.
Also note that (again according to Chris Gessler) IntersectionPointList has half as much items as LineList. Therefore call GetIntersectionPointListElementAt with i / 2. Since i is {0, 2, 4, ... }, i / 2 is {0, 1, 2, ...}.
The for-loop allows you to have a comma-separated list of statements in the first and third section. You could use it to maintain two indexes
for (int i = 0, k = 0; i < LineList.Count - 1; i += 2, k++) {
calcHelper.ForceVanishingPointIntersections(LineList[i], LineList[i + 1]);
AddSprite(
VanishingPointIntersectionList,
calcHelper.GetIntersectionPointListElementAt(k),
greenCirlce);
}
for (int i = 0; i < LineList.Count; i++)
{
calcHelper.ForceVanishingPointIntersections(LineList[i], LineList[i + 1]);
...
i++;
}
Instead of doing that, you should do:
for (int i = 0; i < LineList.Count; i += 2)
{
calcHelper.ForceVanishingPointIntersections(LineList[i], LineList[i + 1]);
...
}
And if LineList.Count isn't even, for example: 3.
It will start at 0, you use 0 and 1.
Then it jumps to 2, you use 2 and 3.
But oops! there isn't 3!
Only count 3! 0 to 2!
Thats when you'll get argument out of range exception.
To avoid that, you can either check if Count % 2 == 0, or you can go until LineList.Count - 1, if you don't mind skipping extra's if there are.
It looks like you're adding two lines per one Sprite. The indexes won't line up. When LineList has 10 items, you will only have 5 IntersectionPointList items, so passing in an index of 9 will fail.
You should change your LineList collection to List<Tuple<string,string>> so that you have two lines per item which will line up with each Sprite. This will create a one-to-one relationship between Lines and Sprites.
I suppose you could always pass in an index (i / 2), which should be the correct Sprite for every two lines.

How to safely check arrays bounds

I am making game of life in 2D array. I need to determine when all adjacent cells are empty so I just test all of them. It works well unless the cell being checked is the boundary. Then of course testing X+1 throws an exception as the index is out of the array boundaries. Can I handle this somehow instead of handling the exception?
Thanks
use GetLength(0) and GetLength(1) to get the width and height of the array.
There is also a neat performance trick the runtime uses for its own bounds checks: casting to unsigned int to reduce the two checks into one. But since this costs readability you use it if the check is really performance critical.
(i >= 0) && (i < length)
becomes
(uint)i < length
If you want speed you'll have to treat the edge-cells differently and process the rest with a
for(int x = 1; x < Size-1; x++) // these all have x-1 and x+1 neighbours
I created an extension method for a 2D array to check if in bounds:
public static class ExtensionMethods
{
public static bool In2DArrayBounds(this object[,] array, int x, int y)
{
if (x < array.GetLowerBound(0) ||
x > array.GetUpperBound(0) ||
y < array.GetLowerBound(1) ||
y > array.GetUpperBound(1)) return false;
return true;
}
}
Yes, before accessing the element at index i + 1, check whether i + 1 is strictly inferior to array.Length.

Categories

Resources