How to handle Index out of range in a better way - c#

My code was giving me Index out of Range exception for a certain input. Below is the problematic code:
string[] snippetElements = magic_string.Split('^');
string a = snippetElements[10] == null ? "" : "hello";
string b = snippetElements[11] == null ? "" : "world";
For that particular input, array snippetElements had only one element in it, hence while trying to index 10th and 11th element, I got the exception.
For now, I have introduced the following check:
if (snippetElements.Length >= 11)
{
string a = snippetElements[10] == null ? "" : "hello";
string b = snippetElements[11] == null ? "" : "world";
}
Can someone suggest a better way to write this check. Somehow the number 11 is not looking good in the code.

Yes this is an old post, but still helpful. You could use this which I think is cleaner:
string a = snippetElements.ElementAtOrDefault(10) ?? "hello";
string b = snippetElements.ElementAtOrDefault(11) ?? "world";

Can someone suggest a better way to write this check. Somehow the
number 11 is not looking good in the code.
Well you are accessing the element with 11 index, if you have that index in your variable then you can use that in your check, otherwise 11 is fine in your check. Your check should be if(index < snippetElements.Length)
Something like:
int index = 11;
if(index < snippetElements.Length)
{
string b = snippetElements[index] == null ? "" : "world";
}

snippetElements[11] is the 12th element.
if (snippetElements.Length >= 12)
As long your are actually using the [10] and [11] indexes it doesn't look wrong to use the 12 in the if statement.

You can generalize the problem to an extension method like this:
public static class ArrayExtensions
{
public static bool TryIndex<T>(this T[] array, int index, out T result)
{
index = Math.Abs(index);
result = default(T);
bool success = false;
if (array != null && index < array.Length)
{
result = (T)array.GetValue(index);
success = true;
}
return success;
}
}
And convert your code to:
string[] snippetElements = magic_string.Split('^');
string a = null;
string b = null;
if (snippetElements.TryIndex(10, out a) && snippetElements.TryIndex(11, out b))
{
}
Or, more like your source code and using the TryIndex(...) extension method:
string[] snippetElements = magic_string.Split('^');
string a = null;
string b = null;
snippetElements.TryIndex(10, out a);
snippetElements.TryIndex(11, out b);
a = a ?? "hello"; // Null coalesence ?? operator is great!
b = b ?? "world";
It makes the array indexed access safer since your code won't never throw ArgumentOutOfRangeException.
Note that this extension method will work for any kind of array, regardless of its type! Either if its a value type (int, byte...) or a reference type (string, your own classes...).

The logic here is wrong.
If your Split method produces less than 12 elements your indexing on the array snippetElements could go only from zero to (Length - 1) of the array.
In that case there aren't elements at index 10 or 11. And, in any case, if the snippetElement.Lenght is equal or greater than 12, then the elements in the array can't be null. Or they contain a string or they will be empty strings.
You could just write
string a = snippetElements.Length >= 12 ? "hello" : string.Empty;
string b = snippetElements.Length >= 12 ? "world" : string.Empty;

It might be late now, but for any one else who's having the same problem, that's how I solved it. I usually use this logic inside a for loop.
int index = 10;
string a = (index < snippetElements?.Length) ? snippetElements[index] : string.Empty;
snippetElements?.Length checks if snippetElements is not empty then it calls its Length property. Accessing Length property of an empty array results in an exception.
snippetElements[index] (you could replace this with "hello" as in your example) is accessed only if the index is in the bound of an array. Otherwise, it assigns String.Empty to a.

Related

How to compare whether two strings are identical?

So I'm doing this years Advent of Code and I'm stuck on the second day, part 2.
You are given inputs which look like this:
"1-3 c: caaasa"
You have to check how many passwords are valid due to the policy like,
in above example:
letter c has to be in position 1 OR 3 in the string caaasa. If
yes, the password is valid.
I've broken down that string to different sections, and now I try to compare a string "znak" which contains that given letter to a letter on position zakresmin and zakresmax in string "passdiv"
Yet, everytime it returns False, so it doesn't add up to the count of passwords.
I tried using Equals() and CompareTo(), but they don't seem to work.
How can I modify my code so it returns proper values?
var iloschasel = 0;
using (StreamReader sr = new StreamReader(#"C:\Users\Wurf\Desktop\text.txt"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] linia = line.Split(" ");
string zakres = linia[0];
string[] zakresy = zakres.Split("-");
int zakresmin = Convert.ToInt32(zakresy[0]);
int zakresmax = Convert.ToInt32(zakresy[1]);
string znak = (linia[1].Replace(":", "")).Trim();
var suma = Regex.Matches(linia[2], znak);
string passdiv = linia[2];
if(passdiv[zakresmin].Equals(znak) || passdiv[zakresmax - 1].Equals(znak))
{
iloschasel += 1;
}
}
}
Console.WriteLine(iloschasel);
As mentioned, when you call Equals on two different types you are playing a game of chance with how the actual types are implemented. In this case you lose. Strings and chars will never have an equivalence or the same reference.
I believe the compiler or resharper would give you a warning alerting you that neither type derive from string and char
However, I was bored enough to give an alternate solution
public static bool IsValid(string input)
{
var match = Regex.Match(input, #"(\d)-(\d) (\S): (.*)");
if(!match.Success)
throw new ArgumentException( $"Invalid format : {input}",nameof(input));
var first = int.Parse(match.Groups[1].Value);
var second = int.Parse(match.Groups[2].Value);
var c = char.Parse(match.Groups[3].Value);
var password = match.Groups[4].Value;
return password[first-1] == c && password[second-1] == c;
}
Test
Console.WriteLine($"Is Valid = {IsValid("1-3 c: caaasa")}");
Console.WriteLine($"Is Valid = {IsValid("1-3 c: cacaasa")}");
Output
Is Valid = False
Is Valid = True
Note : this is not meant to be a complete bullet-proof solution. Just a novel elegant way to solve your problem
Your problem is that you are comparing a string to a char
var match = "c" == 'c';
Will give a compile error because they are different data types
var match = "c".Equals('c');
will let you compile, but will always return false because a char will never equal a string. You have to turn the char into a string or visa versa for the check to work
var match = "c"[0] == 'c';
So in your if statement, if you fix the check to compare strings with strings or chars with chars you should get some positive results. And also fix your indexing issue to decide if you want a 0 based index or a 1 based index with zakresmin and max
Also as a side note, it can be helpful to step through your code line by line in debug mode, to find out which line isn't behaving like you expect it to. In your case debugging would have helped you zero in on the if statement as a starting point to fixing things.
So it turns out (if I understand that correctly) that a compared element of the string passdiv was a char which I tried to compare to znak which was a string. I added ToString() to my code and it works well. Also fixed the range of zakresmin by subtracting 1 so it works properly.
if((passdiv[zakresmin - 1].ToString() == znak && passdiv[zakresmax - 1].ToString() != znak) || (passdiv[zakresmin - 1].ToString() != znak && passdiv[zakresmax - 1].ToString() == znak))
{
iloschasel += 1;
}

Null-coalescing operator inside ternary

I have this C# code:
data[j].Actual[0] = data[j].Actual.Count != 0 ? (data[j].Actual[0] ?? 0) : 0;
What I want to do is to check if the count is 0, in this case the value of the variable should be set on 0.
In case the count is different of 0, I want to check with null-coalescing operator if that value is there, otherwise return 0.
The problem I have is that I get this error message:
Index was out of range. Must be non-negative and less than the size of
the collection. Parameter name: index
My guess is that even when data[j].Actual.Count is 0 it doesn't the result to 0 but it tries to do something with the null-coalescing operator.
Any suggestions?
I think the problem is the assignment; if data[j].Actual is an empty (list/array/whatever), then you can't just assign data[j].Actual[0] to anything. If this is an array, you can't do anything (except maybe create a new array). If this is a list, you would need to .Add a value.
I would simplify the code:
var actual = data[j].Actual;
if (actual.Count == 0)
{
actual.Add(0);
// or if this is an array: data[j].Actual = new double?[] {0};
}
else if (actual[0] == null)
{
actual[0] = 0;
}
This isn't one line, but it is easy to understand.
look carefully
data[j].Actual[0] = data[j].Actual.Count != 0 ? (data[j].Actual[0] ?? 0) : 0;
You are assigning to Actual[0]. if Actual.Count is 0 you get out of range exception. you cant use ternary. use if statement.
if(data[j].Actual.Count != 0)
{
data[j].Actual[0] = data[j].Actual[0] ?? 0;
}
// else you don't need to set anything because there is nothing to set!

how to check the if condition with string and integer

I want to get result of a value with if condition.
i have get some value in xml file.
now what I want is
if I have a variable "a" here i have assigned some values by using dataset.
and i have another variable "b" is assigned value from xml file.
for example
int a=25;
string b=">10"
now I want to check the condition if condition with out ">" because the symbol present in b variable. I dont know how to check this condition can anybody explain me how to acheive this.
I tried like this but not working
if(a+b)
You can use the DataTable.Compute-"trick" to evaulate such expressions:
int a = 25;
string b = ">10";
bool isTrue = (bool)new DataTable().Compute($"{a}{b}", null); // true
What is supported you can read at the DataColumn.Expression remarks.
if the condition is 1!=10, how to use not equal in this code .this
condition is not working what should i do.
As the documentation tells you that is not valid syntax, you have to use <> (look at operators). So a simple approach would be either to use <> in the first place or replace them:
b = b.Replace("!=", "<>");
You can have some function to remove non numeric characters:
public int Parse(string x)
{
x = Regex.Replace(x, "[^0-9.]", "");
int result = 0;
int.TryParse(x , out result);
return result;
}
If its always a number with a symbol then:
symbol = b[0];
int bval = int.Parse(b.Substring(1))
And considering your comment for comparison you can do:
if((symbol=='>'&&a>b)||
(symbol=='='&&a==b)||
(symbol=='<'&&a<b)
){
//do your magic here
}
Of course you may need only one of < = > or you may need to have separate if conditions for each, what ever suits your needs, but I just wanted to give the idea.
I tried like this
if (b.Contains(">")) {
b = b.Replace(">", "");
if (a >Convert.ToInt32(b))
{
Console.WriteLine("value is less");
}
else
{
Console.WriteLine("value is Greater");
}
}
similarly all the symbols
First separate symbol from b:
string symbol = b[0].ToString();
string numberString = b.SubString(1);
int number = int.Parse(numberString);
Now use switch to get operation for symbol and compare:
bool result = false;
switch (symbol)
{
case ">":
if (a > number)
{
result = true;
}
break;
}
EDIT: Changed symbol declaration to avoid error: "cannot implicit convert type char to string"

Check if array is null or empty?

I have some problem with this line of code:
if(String.IsNullOrEmpty(m_nameList[index]))
What have I done wrong?
EDIT: The m_nameList is underlined with red color in VisualStudio, and it says "the name 'm_nameList' does not exist in the current context"??
EDIT 2: I added some more code
class SeatManager
{
// Fields
private readonly int m_totNumOfSeats;
// Constructor
public SeatManager(int maxNumOfSeats)
{
m_totNumOfSeats = maxNumOfSeats;
// Create arrays for name and price
string[] m_nameList = new string[m_totNumOfSeats];
double[] m_priceList = new double[m_totNumOfSeats];
}
public int GetNumReserved()
{
int totalAmountReserved = 0;
for (int index = 0; index <= m_totNumOfSeats; index++)
{
if (String.IsNullOrEmpty(m_nameList[index]))
{
totalAmountReserved++;
}
}
return totalAmountReserved;
}
}
}
If m_nameList is null, that will still blow up, because it will try to find the element to pass to String.IsNullOrEmpty. You'd want:
if (m_nameList == null || String.IsNullOrEmpty(m_nameList[index]))
That's also assuming that index is going to be valid if m_nameList is non-null.
Of course, this is checking if the element of an array is null or empty, or if the array reference itself is null. If you just want to check the array itself (as your title suggests) you want:
if (m_nameList == null || m_nameList.Length == 0)
EDIT: Now we can see your code, there are two problems:
As Henk showed in his answer, you're trying to use a local variable when you need a field
You're also going to get an ArrayIndexOutOfBoundsException (once you've used a field) due to this:
for (int index = 0; index <= m_totNumOfSeats; index++)
That will perform m_totNumOfSeats + 1 iterations because of your bound. You want:
for (int index = 0; index < m_totNumOfSeats; index++)
Note that m_nameList[m_totNumOfSeats] is not valid, because array indexes
start at 0 in C#. So for an array of 5 elements, the valid indexes are 0, 1, 2, 3, 4.
Another option for your GetNumReserved method would be to use:
int count = 0;
foreach (string name in m_nameList)
{
if (string.IsNullOrEmpty(name))
{
count++;
}
}
return count;
Or using LINQ, it's a one-liner:
return m_nameList.Count(string.IsNullOrEmpty);
(Are you sure you haven't got it the wrong way round though? I would have thought reservations would be the ones where the name isn't null or empty, not the ones where it is null or empty.)
If it's the wrong way round, it would be this instead in LINQ:
return m_nameList.Count(name => !string.IsNullOrEmpty(name));
After Edit2:
You are defining m_nameList as a local variable of the constructor.
The rest of your code needs it as a field:
class SeatManager
{
// Fields
private readonly int m_totNumOfSeats;
private string[] m_nameList;
private double[] m_priceList;
// Constructor
public SeatManager(int maxNumOfSeats)
{
m_totNumOfSeats = maxNumOfSeats;
// Create arrays for name and price
m_nameList = new string[m_totNumOfSeats];
m_priceList = new double[m_totNumOfSeats];
}
....
}
To avoid the error you can perform some pre conditions in the if, like these :
if(m_nameList == null || index < 0 || m_nameList.Length < index || String.IsNullOrEmpty(m_nameList[index]))
This should works fine(without causing error) in almost any conditions ...

convert string[] to int?[]

how to convert a string array to nullable integer array.
Well, the two aspects are:
How do you convert an array from one type to another?
How do you convert a string to a nullable int?
The first is simple - you can use Array.ConvertAll, passing in an Converter delegate. There's the LINQ way as well (x.Select(...).ToArray()), but that's slightly less efficient if you know you've got an array to start with and you want an array out of the other end 1.
The string to int? is probably best done in a method. You can do it in an anonymous function, but in this particular case I'd use a separate method. I rarely like to have to declare variables in an anonymous function.
public static int? TryParseInt32(string text)
{
int value;
return int.TryParse(text, out value) ? value : (int?) null;
}
To answer the comment, yes you need the cast 2.
or without the conditional:
public static int? TryParseInt32(string text)
{
int value;
if (int.TryParse(text, out value))
{
return value;
}
else
{
return null;
}
}
Put them together like this:
string[] x = {"15", "10", "hello", "5" };
int?[] y = Array.ConvertAll(x, TryParseInt32);
1 Array.ConvertAll is more efficient than x.Select().ToArray() because it has more information. It can create the final array immediately - no resizing is ever required. Calling Enumerable.ToArray() (with the result of Select()) doesn't have the same information, so it effectively has to add the results to a List<T> (potentially resizing several times along the way) and then usually resize at the end as well. (It doesn't actually use List<T> but the concepts are the same.) It's definitely worth knowing about the Select/ToArray solution, but I don't think it's actually any more readable than ConvertAll in this case, so you might as well use the more efficient form.
2 The reason you need the cast in the conditional is because the compiler doesn't know what type of "null" you really mean - but it knows it can't convert from null to an int. The type of the conditional expression has to be either the type of the second operand or the type of the third operand, and the other one has to be implicitly convertibly. By forcing the third operand to be of type int? here everything is fine - because you can implicitly convert from int to int?. Note that the compiler doesn't take the way you're using the expression to try to work out the type. Another alternative is to use new int?() instead of null.
By parsing and using Linq:
string[] input = { "1", "3", "x" }
var result = input.Select(
s => { int i; return (int.TryParse(s, out i)) ? i : (int?) null; }).ToArray();
… but I grant that this is a bit cryptic. I wouldn’t use a lambda expression here. For clarity, this should belong to a proper function ParseNullableInt. Then you can call it like this:
var result = input.Select(ParseNullableInt).ToArray();
Edit: I removed the null/empty check because TryParse properly handles those cases by returning false.
string[] stringArray = ...;
int?[] nullableIntArray = Array.ConvertAll(stringArray,
s =>
{
int value;
if (!int.TryParse(s, out value))
return default(int?);
return (int?)value;
});
One option would be this (I'm presuming that you want nulls for the values that were not integer values):
public static class StringArrayExtension
{
public static int?[] ToNullableIntArray(this string[] array)
{
int?[] result = new int?[array.Length];
for (int index = 0; index < array.Length; index++)
{
string sourceValue = array[index];
int destinationValue;
if (int.TryParse(sourceValue, out destinationValue))
{
result[index] = destinationValue;
}
else
{
result[index] = null;
}
}
return result;
}
}
usage:
string[] source = { "hello", "1", "3.1415", "20", "foo" };
int?[] dest = source.ToNullableIntArray();
You need to use int.TryParse:
string[] s = { "123", "a", "45" };
int?[] result = new int?[s.Length];
for (int i = 0; i < s.Length; ++i)
{
int a;
if (int.TryParse(s[i], out a))
result[i] = a;
}
List<int?> ints = new List<int?>();
foreach (string str in strs)
{
int val;
if (int.TryParse(str, out val))
{
ints.Add(val);
}
else { ints.Add(null); }
}
return ints.ToArray();
An integer array can not be null. In fact every element in Integer Array will have "0" by default.
Regarding Conversion
Create an Integer array of the same length or an arraylist
int [] _arr=new int[strArray.length];
for(int i=0;i<strArray.length;i++]
{
if(String.IsNullOrEmpty(strArray[i]))
{
_arr[i]=0;
}
else
{
_arr[i]=int.Parse(strArray[i]);
}
}

Categories

Resources