HtmlAgilityPack - How do I add data to an array? - c#

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();

Related

JSON data List to combo box

I know this has been asked quite a few times and I know ot will be really simple but I'm really new to C# and I'm pulling my hair out because I've been coding (not very well) through the night. I have a class ProcessOrdersActive that I Deserialiaze to details. It's falling over when I try to add ProcessOrderNbr[I] to the combobox.
//Deserialise data
ProcessOrdersActive details = JsonConvert.DeserializeObject<ProcessOrdersActive>(responseBody);
var ordersList = new List<ProcessOrdersActive>();
ordersList.Add(details);
int numofitems = ordersList.Capacity;
txtActiveOrders.Text = numofitems.ToString();
for (int i = 0; i < numofitems; i++)
{
comboBoxOrders.Items.Add (details.ProcessOrderNbr[i]);
}
Many thanks for the responses. I had a good chat with a proficient C# programmer today and here is the solution that he came to. The names are slightly different from the original post.
//Deserialise data & send to DataGrid
ProcessOrderDetails details = JsonConvert.DeserializeObject<ProcessOrderDetails>(responseBody);
//Get number of operations and display to screen
int numofitems = details.MaterialList.Count<Materiallist>();
txtNumOfMaterials.Text = numofitems.ToString();
//Find the last operation
var lastone = details.MaterialList.Last<Materiallist>();
//Create a new material/operation list
var materialList = new List<Materiallist>();
//Add the last operation to the list
materialList.Add(lastone);
//Parse the list to the data grid
dataGridProcessOrderDetails.DataSource = materialList;
You are trying to access an index that may be out of bounds of the array/list.
The number of items should be the Length/Count of the array/List you are accessing.
//Deserialise data
ProcessOrdersActive details = JsonConvert.DeserializeObject<ProcessOrdersActive>(responseBody);
var ordersList = new List<ProcessOrdersActive>();
ordersList.Add(details);
int numofitems = details.ProcessOrderNbr.Count;//If this is a list use Count. If it is an array use Length
txtActiveOrders.Text = numofitems.ToString();
for (int i = 0; i < numofitems; i++) {
comboBoxOrders.Items.Add (details.ProcessOrderNbr[i]);
}

how to make loop with a list work, that finds new elements? c# webdriver [duplicate]

I am getting "index out of range" from this loop. But I need to use new elements that loop founds, how do I do that? Please help to fix the problem
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
string[] links = new string[linkCount];
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
links[i] = linksToClick[i].GetAttribute("href");
}
I think that you could refactor your code:
var linkElements = driver.FindElements(By.CssSelector("a[href]")).ToList();
var links = new List<string>();
foreach (var elem in linkElements)
{
links.Add(elem.GetAttribute("href"));
}
If that works, you could simplify the query:
var instantLinks = driver.FindElements(By.CssSelector("a[href]"))
.Select(e => e.GetAttribute("href"))
.ToList();
You can rewrite your code to bypass the for loop:
string[] links = driver.FindElements(By.CssSelector("a[href]")).Select(l => l.GetAttribute("href")).ToArray();
This should also avoid the index out of range problem, and cut down the amount of code you have to write.
First of all i dont see a point in assigning linkstoclick values inside loop... And Reason for error must be that linksToClick list's length is more than that of linkCount.
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
List<string> links = new List<string>();
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
if (linksToClick.Count < i)
links.Add(linksToClick[i].GetAttribute("href"));
}
This might help with the out of range exception.
Doing this allows you to create a list of type: string without having to explicitly define the size of the list
the first one gets all of your elements by tag name ...let's assume 5.
in the loop, your driver get's all the elements by css selector, and you might have a different number here. let's say 4.
then, you might be trying to set the fifth element in a four element array.
boom.
Easiest fix to debug:
int linkCount = driver.FindElements(By.TagName("a")).Count;
string[] links = new string[linkCount];
// WRITE OUT HOM MANY links you have
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
// ASSERT THAT YOU HAVE THE SAME AMOUNT HERE
If (links.Count != linksToClick.Count)
// your logic here
links[i] = linksToClick[i].GetAttribute("href");
}

How do I add for() loop index identifier to label name?

I am attempting to dynamically add content to labels in WPF from an object in a list.
I can add the content from the Object fine by hard-coding the index number:
// assign forecast values to 5 user interface labels
Forecast fc1 = day.Weather.getForecastInList(0);
day1Label.Content = fc1.Day;
day1ConditionLabel.Content = fc1.ForecastCondition;
day1TempLabel.Content = formatHiLow(fc1.TemperatureHigh, fc1.TemperatureLow);
Forecast fc2 = day.Weather.getForecastInList(1);
day2Label.Content = fc2.Day;
day2ConditionLabel.Content = fc2.ForecastCondition;
day2TempLabel.Content = formatHiLow(fc2.TemperatureHigh, fc2.TemperatureLow);
etc...
But then I would have to repeat a lot of the assignment code for each of the 5 labels.
I have attempted to use a for() loop which works fine to get each of the objects from the list but won't let me rename the labels e.g.
// assign forecast values to 5 user interface labels
for (int i = 0; i < 5; i++ )
{
Forecast fc+(i+1) = day.Weather.getForecastInList(i);
day+(i+1)+Label.Content = fc+(i+1)+.Day;
}
How can I dynamically identify WPF labels using the index in a for() loop?
I don't recommend this but to answer your question, theres a possibility:
Create a list which contains all your labels:
List<Label> dayLabels = new List<Label>();
dayLabels.Add(day1Label);
dayLabels.Add(day2Label);
//and so on...
Loop through all the labels:
for (int i = 0; i < 5; i++ )
{
Forecast fc = day.Weather.getForecastInList(i);
dayLabels.ElementAt(i).Content = fc.Day;
}

c# Arrays - Index Out of Range Exception

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.

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.

Categories