c# Arrays - Index Out of Range Exception - c#

I am getting indexoutofrangeexception (see ----> pointer for the line generating the error down below in the code). The program loops through the header and line item records in a dataset tables. The tables have a relationship. My sample data has 2 headers, each with 2 lines. The progam has two loops, the first one loops through the header records and the second one loops through the child records of the header.
Part of the program:
// ***** PO Header and Line
int ln;
ln = 0;
// Create an eConnect PO Header node object
taGLTransactionHeaderInsert jeh = new taGLTransactionHeaderInsert();
// Create an array for lineitems
taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[] lineitems = new taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[ln];
foreach (DataRow dtrHDR in ds.Tables["Header"].Rows)
{
Array.Clear(lineitems, 0, ln);
jeh.BACHNUMB = "Sheraz";
jeh.JRNENTRY = jenoint;
jeh.REFRENCE = dtrHDR["Reference"].ToString();
jeh.SOURCDOC = dtrHDR["AvantisJE"].ToString();
jeh.USERID = System.Environment.UserName;
jeh.TRXDATE = System.DateTime.Now.ToString();
ln = 0;
foreach (DataRow dtrLine in dtrHDR.GetChildRows("HdrLine"))
{
// Populate the elements of the taPoLIne_ItemsTaPOLine XML node
taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert jel = new taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert();
jel.BACHNUMB = jeh.BACHNUMB;
jel.JRNENTRY = jeh.JRNENTRY;
jel.ACTNUMST = dtrLine["GreatPlains"].ToString();
jel.DEBITAMT = Convert.ToDecimal(dtrLine["Debit"].ToString());
//Avantis Inv Trx Key
jel.ORDOCNUM = dtrLine["AvantisJE_Line"].ToString();
// Avantis GL Trx Type
jel.ORTRXDESC = dtrLine["transactiontypename"].ToString();
//Add POLine to an Array
lineitems[ln] = jel; ----------------> I get an error here!
ln = ln + 1;
Array.Resize(ref lineitems, ln + 1);
}
}

You are accessing an index that doesn't yet exist.
//Add POLine to an Array
lineitems[ln] = jel; ----------------> I get an error here!
ln = ln + 1;
Array.Resize(ref lineitems, ln + 1);
You need to change the order to :
//Add POLine to an Array
Array.Resize(ref lineitems, ln + 1);
lineitems[ln] = jel; ----------------> should be fixed, no error here!
ln = ln + 1;
EDIT: Now that the immediate problem is out of the way, on to a better implementation.
Arrays are of a fixed sized, resizing an array is an expensive operation (basically it entails creating a copy with a new size). typically you would use these after identifying a performance bottle neck. In most cases it would be much better to use a List.
I'd recommend changing this line:
// Create an array for lineitems
taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[] lineitems =
new taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[ln];
to:
var lineitems = new List<taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert>();
and then to add to it you simply do
lineitems.Add(jel);
to iterate over them would be:
for (var ln in lineitems) {
// whatever you want to do with a line.
}
to acccess a specific item by index would be:
lineitems.Item(i); // get the ith item in the list.

This is because you created an array with 0 elements and try to insert an element on position 0. This will not work. You can fix it by declaring the array with a size of 1 to begin with:
// Create an array for lineitems
taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[] lineitems = new taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[1];
However, resizing an array on the fly is not the idiomatic .NET way of doing this. You could use a List<T>, which takes care of resizing for you, and leaving you with cleaner code and possibly better performance.

It appears that you're trying to increase the size of the array by setting a value, like you might do in JavaScript. C# arrays are not like that. You need to create them at the size you want them to be when finished.
Alternately, you could use a List object, using the Add() method to put new content into the list.

lineitems is obviously not the same size as the row collection returned by dtrHDR.GetChildRows("HdrLine"). You are creating an array of zero elements and then trying to index into it. If you want it to match the size of dtrHDR.GetChildRows("HdrLine") then you need to call that first and initialize the array after you can get the count.
Instead of using an array why don't you use a List<T> and just push items onto it? No need to worry about IndexOutOfRange exceptions anymore.

You need to initialize the array before putting anything in to it. call the .resize first.

those lines are the problem
int ln;
ln = 0;
// Create an array for lineitems
taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[] lineitems = new taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[ln];
You create table of 0 elements.

You have an off-by-one error in your program:
int ln = 0
.
.
.
taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[] lineitems = new taGLTransactionLineInsert_ItemsTaGLTransactionLineInsert[ln];
.
.
.
lineitems[ln] = jel;
You are initializing an array of 0 elements, then trying to set the first element (element[0]) to a value.

Related

HtmlAgilityPack - How do I add data to an array?

I am trying to add data to an array but everytime I try to add new data, the old data gets deleted. I have 20 pages and the array does not add all the 20 pages but only 1. The rest just gets deleted.
HtmlNode[] array = null;
for (int i = 0; i <= 10; i++)
{
var doc1 = web.Load(loopingpage);
array = doc1.DocumentNode.SelectNodes("//*[contains(#class, '-row-cp')]").ToArray();
}
The data does not get added to the array. It only displays the current data.
You overwrite the array in each iteration in the for loop in this line.
array = doc1.DocumentNode.SelectNodes("//*[contains(#class, '-row-cp')]").ToArray();
You could of course add nodes to a predefined array, but arrays are fixed in size, so you would need to know beforehand how many nodes there are to allocate an appropriate size. Instead, you can use a List<T>, which is not fixed-size and more suitable for your scenario.
// Create a new list of nodes
var nodes = new List<HtmlNode>();
for (var i = 0; i <= 10; i++)
{
var doc1 = web.Load(loopingpage);
var doc1Nodes = doc1.DocumentNode.SelectNodes("//*[contains(#class, '-row-cp')]");
// Add all selected nodes from doc1 to the list
nodes.AddRange(doc1Nodes);
}
// Convert it to an array later if needed
var nodesArray = nodes.ToArray();

List<List> confusion

snippets of my code
List<List<optionsSort>> stocks = new List<List<optionsSort>>();
optionsSort tempStock1 = new optionsSort();
List<optionsSort> stock = new List<optionsSort>();
then some code,
for (int j = 1; j < optionsSt.Count; j++)
{
if (optionsSt[j].isin == optionsSt[j - 1].isin)
{
tempStock1.name = optionsSt[j].name;
tempStock1.date = optionsSt[j].date;
tempStock1.strike = optionsSt[j].strike;
tempStock1.size = optionsSt[j].size;
tempStock1.isin = optionsSt[j].isin;
tempStock1.callPut = optionsSt[j].callPut;
stock.Add(tempStock1);
}
else
{
stocks.Add(stock);
k = k + 1;
stock.Clear();
tempStock1.name = optionsSt[j].name;
tempStock1.date = optionsSt[j].date;
tempStock1.strike = optionsSt[j].strike;
tempStock1.size = optionsSt[j].size;
tempStock1.isin = optionsSt[j].isin;
tempStock1.callPut = optionsSt[j].callPut;
stock.Add(tempStock1);
}
}//endfor
Basicly, im going through a large list to sort elements into groups, a new List name stocks.
now the problem is, when I add to stocks all elements contained in the list stock and then clear stock on the next line to start again, I delete all the elements I have stored in stocks.
Any Ideas. Do I have to index stocks like stocks[i].Add(stock) so each block of similar stocks is an element in stocks.
Thanks for any help.
The problem is that List<T> objects, like all classes in .NET, are reference types. That means that every time you add stock to stocks you aren't adding a new list, you are only adding a reference to the same list in memory. So when you later call Clear, that is reflected both in your variable stock and in all other references in stocks.
You can resolve this by making a shallow copy of stock every time you add it to stocks:
stocks.Add(stock.ToList());
You're not creating a new list, you're using one list, and filling it and clearing it repeatedly. Since your outer list contains only one list, repeated multiple times, that list will have the same contents in every instance. That is, when you clear your list, you can no longer access the old contents, even if you try to access them from inside the outer list.
What you need to do is to change this line:
stock.Clear();
To this:
stock = new List<optionsSort>();
That is what you really meant. :)

Adding and Couting items in a list

Let's say I have a file that looks like this:
R34 128590 -74.498 109.728 0 0805_7
R33 128590 -74.498 112.014 0 0805_7
R15 128588 -68.910 127.254 0 0805_7
R32 128587 -65.354 115.189 0 0805_7
R35 128587 -65.354 117.348 0 0805_7
R38 128590 -65.354 119.507 0 0805_7
What I want to do is add the 2nd column to a list and have a counter count how many times that item occurs and outputs it with the number and then the counted amount of that number.
Is there a way to do this using a List? If so, how could I go about doing that?
I have tried messing around with things and this is where I was heading.. but it does not work properly
int lineCount = 1;
int itemCounter = 0;
foreach (var item in aListBox.Items)
{
// Creates a string of the items in the ListBox.
var newItems = item.ToString();
// Replaces any multiple spaces (tabs) with a single space.
newItems = Regex.Replace(newItems, #"\s+", " ");
// Splits each line by spaces.
var eachItem = newItems.Split(' ');
###
### HERE is where I need help ###
###
List<string> partList = new List<string>();
partList.Add(eachItem[1]);
if (partList.Contains(eachItem[1]))
itemCounter++;
else
partList.Add(eachItem[1]);
sw.WriteLine(lineCount + ": "+ partList + ": " + itemCounter);
lineCount++;
}
SO for the example above, it would output this:
1: 128590: 3 #lineCount, partList, itemCounter
2: 128588: 1
3: 128587: 2
Can someone help me figuring out how to properly do this?
use linq with count and group by (see Count- Grouped section).
create your partList outside the foreach loop and add each item to it inside the loop , so that it would contain all of the elements:
List<string> partList = new List<string>();
foreach (var item in aListBox.Items)
{
//regex stuff here...
partList.Add(eachItem[1]);
}
(in your example- {128590, 128590, 128588, 128587, 128587, 128590})
and then use LINQ to output the result-
var elementsWithCounts = from p in partList
group p by p into g
select new { Item = g.Key, Count = g.Count()};
I would either use a Linq query or a Dictionary
something like
List<string> items = new List<string>{"128590", "128590", "128588", "128587", "128587", "128590"};
Dictionary<string,int> result = new Dictionary<string,int>();
foreach( int item in items )
{
if(result.ContainsKey(item) )
result[item]++;
else
result.Add(item,1);
}
foreach( var item in result )
Console.Out.WriteLine( item.Key + ":" + item.Value );
Once you have the items split by space, I'm assuming you have a string array looking like so:
[0] = "R34"
[1] = "128590"
[2] = "-74.498"
[3] = "109.728"
[4] = "0"
[5] = "0805_7"
You can simply perform this operation with a Group By operation.
var items = aListBox.Items.Select(x => /* Split Code Here and Take Element 1 */).GroupBy(x => x);
foreach(var set in items)
{
Console.WriteLine(set.Key + " appeared " + set.Count() + " times.");
}
Basically, you are trying to do this by iterating once, and that is not really going to work, you are going to have to iterate twice, otherwise you will wind up doing an output every time you loop in the foreach, and even if your accurate you are going to be outputting a new line each time. If you need to really use a List instead of a keyed dictionary or hashtable which would be idea for this (key = number, value = count), then you need to build the list first, then summarize the list. You can either use LINQ Group By (which is a bit terse), or create a function that does something similar to what you already have. If you are trying to learn concepts, look at the code below, it could be more condensed but this should be fairly easy to read.
List<string> partList = new List<string>();
List<string> displayedNumbers = new List<int>();
// Build the original list first.
foreach (var item in aListBox.Items)
{
// Creates a string of the items in the ListBox.
var newItems = item.ToString();
// Replaces any multiple spaces (tabs) with a single space.
newItems = Regex.Replace(newItems, #"\s+", " ");
// Splits each line by spaces.
var eachItem = newItems.Split(' ');
partList.Add(eachItem[1]);
}
// Now run through that list and count how many times the same number occurs.
// You will need two loops for this since your list is a single dimension collection.
foreach(var number in partList)
{
var innerList = partList;
// set this to zero because we are going to find at least 1 duplicate.
var count = 0;
foreach(var additionalNumber in innerList)
{
if(additionalNumber == number)
{
// If we find anymore increase the count each time.
count += 1;
}
}
// Now we have the full count of duplicates of the outer number in the list.
// If it has NOT been displayed, display it.
if(!displayedNumbers.Contains(number))
{
sw.WriteLine(partList + ": " + count);
displayedNumbers.Add(number);
}
}
Use a hash table instead ofa list. You can save the key as 128590,... and the value the number of times it has occurred.
Before you insert the new value check if it is already present in the hashtable by using the Contains operation and if it is increment the value.
I think the biggest problem is getting from raw lines of your text field to individual values. My guess is this is a tab-delimited file with a known constant number of columns, in which case you could use String.Split() to separate the sub-strings. Once you have the strings separated, you can count the instances of the proper column pretty easily with a little LINQ. Given a list or collection of your file's lines:
var histogram = myListOfLines
//Split each string along spaces or tabs, and discard any zero-length strings
//caused by multiple adjacent delimiters.
.Select(s=>s.Split(new[]{'\t',' '}, StringSplitOptions.RemoveEmptyEntries))
//Optional; turn the array of strings produced by Split() into an anonymous type
.Select(a=>new{Col1=a[0], Col2=a[1], Col3=a[2], Col4=a[3], Col5=a[4]})
//Group based on the values of the second column.
.GroupBy(x=>x.Col2)
//Then, out of the grouped collection, get the count for each unique value of Col2.
.Select(gx=>new{gx.Key, gx.Count()});

Adding an Element to an Array

Ok, I cannot get this. I've looked at it and I don't see why it's out of bounds. I get the error at paypalItems[paypalItems.Length] = new PaymentDetailsItemType
PaymentDetailsItemType[] paypalItems = new PaymentDetailsItemType[order.OrderItems.Count];
for (int i = 0; i < order.OrderItems.Count; i++)
{
paypalItems[i] = new PaymentDetailsItemType
{
Name = order.OrderItems[i].Name,
Amount = ApiUtility.CreateBasicAmount(order.OrderItems[i].Price),
Description = order.OrderItems[i].Name,
Number = order.OrderItems[i].Sku,
};
}
// paymentItems now has 1 item...now to the if statement:
if (giftCardsTotal != 0)
{
// add Coupons & Discounts line item
paypalItems[paypalItems.Length] = new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
};
}
UPDATED: I changed the size of the array..now it's all good. No need for the -1
PaymentDetailsItemType[] paypalItems = new PaymentDetailsItemType[order.OrderItems.Count + 1];
for (int i = 0; i < order.OrderItems.Count; i++)
{
paypalItems[i] = new PaymentDetailsItemType
{
Name = order.OrderItems[i].Name,
Amount = ApiUtility.CreateBasicAmount(order.OrderItems[i].Price),
Description = order.OrderItems[i].Name,
Number = order.OrderItems[i].Sku,
};
}
// paymentItems now has 1 item...now to the if statement:
if (giftCardsTotal != 0)
{
paypalItems[paypalItems.Length -1] = new PaymentDetailsItemType
{
Name = "Certificates",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Certificates"
};
}
I think you might be better off using a List<>, so you would declare:
List<PaymentDetailsItemType> paypalItems = new List<PaymentDetailsItemType>();
Then when you iterate through order.OrderItems, just do a paypalItems.Add()
Finally on your if(giftCardsTotal != 0) conditional just do a:
paypalItems.Add(new PaymentDetailsItemType...);
This way you don't have to worry about off by one array indexing issues. Here would be the rewritten code (I use var for my convenience):
var paypalItems = new List<PaymentDetailsItemType>();
foreach (var orderitem in order.OrderItems)
{
paypalItems.Add(new PaymentDetailsItemType
{
Name = orderitem.Name,
Amount = ApiUtility.CreateBasicAmount(orderitem.Price),
Description = orderitem.Name,
Number = orderitem.Sku,
});
}
if (giftCardsTotal != 0)
{
// add Coupons & Discounts line item
paypalItems.Add(new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
});
}
Per John Saunders' suggestion (in the comments), here's a Linq alterative to the variable declaration and first loop:
var paypalItems =
(from orderitem in order.OrderItems
select new PaymentDetailsItemType
{
Name = orderitem.Name,
Amount = ApiUtility.CreateBasicAmount(orderitem.Price),
Description = orderitem.Name,
Number = orderitem.Sku
}).ToList();
Addition based on comment: If you need an Array when you're done, call:
paypalItems.ToArray()
Arrays are zero-based in C#. Using array[array.Length] will always fail. You want Length-1.
In addition, I see that you're trying to expand the array. You can't do that! Once an array is instantiated, it's length cannot change.
If you need a collection that can expand, use List<PaymentDetailsItemType>.
Because they are 0-indexed, so first one is the 0 and last one is length-1.
This is true for almost any programming language..
It seems that first you fill up an array by trasforming elements from another one and then you want to replace last one. Maybe you intended to append an element to the end?
In that case you should build it larger:
PaymentDetailsItemType[] paypalItems = new PaymentDetailsItemType[order.OrderItems.Count+1];
then fill it as you did:
for (int i = 0; i < order.OrderItems.Count; i++)
{
...
}
then set the last one:
paypalItems[paypalItems.Length-1] = ..
paypalItems[paypalItems.Length] =
should be
paypalItems[paypalItems.Length - 1] =
This:
paypalItems[paypalItems.Length]
Will always be outside the bounds of the array as the array index starts at 0. If you want to store something in the last element of the array do:
paypalItems[paypalItems.Length-1] = ...
It looks like you're trying to append to the array by simply using the next index. Perhaps you learned this from exposure to a language like javascript or python, where "arrays" are really complex objects. Real arrays don't work like that.
Instead, you should try using the .Add() method of List<PaymentDetailsItemType>.
You need to change the size of array before add a new array item.
if (giftCardsTotal != 0)
{
// Increase the size
Array.Resize(ref paypalItems, paypalItems.Length + 1);
// add Coupons & Discounts line item
paypalItems[paypalItems.Length -1] = new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
};
}
Length starts from 1 and index from 0. So you should try:
paypalItems[paypalItems.Length - 1]
You are initializing the array to the length of order.OrderItems.Count then after filling it up with data, you are trying to add an additional item to the array. This won't work like this. You have allocated a static size to the array here, you can't add an additional item to that without some sort of reallocation.
You need to create a dynamically allocated array or use a List<T>
To add to the other answers, an Array in .NET is a low-level data type that maps directly to an allocated sequence of bytes in memory. Thus, you cannot add items to an array if it's already full. Maybe what you're looking for is to use a List<T>:
var paypalItems = new List<PaymentDetailsItemType>(order.OrderItems.Count);
// specifying that count is optional, but will increase performance because array space will have to be reallocated less often
// ...
paypalItems.Add(new PaymentDetailsItemType
{
Name = "Gift Cards",
Amount = ApiUtility.CreateBasicAmount(giftCardsTotal),
Description = "Gift Cards"
});
paypalItems[paypalItems.Length] would access one position above the allocated space. If you want the last position you need paypalItems[paypalItems.Length-1]
If you want to append an item use a List<> instead of an array.

Perserving Array from AnonymousType

I looked online for some references but didn't have too much luck. Hopefully it's just some simple thing I'm overlooking, but in my code I'm looping through the participant list and storing the queried results into the array. As you can tell, my foreach statement will just add the last element of the array since the first one is replaced.
How can I just add the value to the next array index. So, if there are [2] in the array, how can I make sure that this.physEntityPhysicalLeftEntityIDs contains [2] instead of just always [1]? If I need to clarify, let me know.
if (leftParticipants.Length >= 0) // Check to see if LeftParticipants exists
{
for (int c = 0; c < leftParticipants.Length; c++)
{
var l_entityIDs =
from BioList in o_data.Descendants(bp + "physicalEntityParticipant")
where BioList.Attribute(rdf + "ID").Value == leftParticipants[c].TrimStart('#')
select new
{
o_entityIDs = BioList.Elements(bp + "PHYSICAL-ENTITY")
.Select(entityID => entityID.Attribute(rdf + "resource").Value).ToArray()
};
foreach (var entity in l_entityIDs)
{
this.physEntityPhysicalLeftEntityIDs = entity.o_entityIDs; // Set SmallMolecules from Left
}
}
}
If physEntityPhysicalLeftEntityIDs is an array, you'll need to initialize an index variable and increment it each time through the foreach loop:
int destIndex = 0;
foreach (var entity in l_entityIDs)
{
this.physEntityPhysicalLeftEntityIDs[destIndex] = entity.o_entityIDs; // Set SmallMolecules from Left
++destIndex;
}
That assumes that you've allocated enough space in the array. If there are more items than will fit in the array, you're going to get an index out of bounds error.
To make sure there's enough space in the array, you can allocate it before the loop above:
this.physEntityPhysicalLeftEntityIds = new int[l_entityIDs.Count()];
Replace int in that line with the proper type (you didn't say what type is being stored in the array).
Well if you want to treat it like an array/list, all you have to do is
l_enityIDs.ToList()
and then .Add(new {o_entityIDs = foo})
If you want to add it to the IEnumerable, that requires an extension method that returns everything in the source enumberable, and a yield statement adding on your next value.

Categories