Why is this ArrayList duplicating he rows - c#

I am junior developer and I am trying to populate an ArrayList from a Dictionary. My problem is rather then adding a new record to the ArrayList it adds the new record but also overwrites the values for all the other values in the array.
So if I inspect the values as the ArrayList is being populated I see the values from the Dictionary as expected. But when that row is inserted into the ArrayList all of the existing rows are over written with the data from current Dictionary Row. So I end up with an ArrayList with several rows that are a duplicate of the last record added from the dictionary. My code is shown below. Can someone please tell me what am I doing wrong? Code below
ArrayList arrData = new ArrayList();
eSummary edata = new eSummary();
//Starts with the first 50 recods retrieved and adds them to the ArrayList. Loops thru to get remaining records
while (blnEmpty)
{
if (response.IsSuccessStatusCode)
{
string json = response.Content.ReadAsStringAsync().Result;
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<Dictionary<string, dynamic>>(json);
for (int i = 0; i < dict.Values.Sum(x => x.Count); i++)
{
foreach (var item in dict)
{
string checkId = (dict["data"][i]["Id"]);
edata.Id = dict["data"][i]["Id"];
edata.idExternal = (dict["data"][i]["idExternal"]) == null ? "" : (dict["data"][i]["idExternal"]);
edata.Type = "Video";
edata.ownerId = (dict["data"][i]["uploadedByOwnerId"]);
edata.dateUploaded = Convert.ToDateTime((dict["data"][i]["dateUploaded"]));
edata.durationSeconds = Convert.ToDouble((dict["data"][i]["durationSeconds"]));
edata.category = (dict["data"][i]["categories"]).Count < 1 ? string.Empty : (dict["data"][i]["categories"][0]);
edata.title = (dict["data"][i]["title"]) == string.Empty ? string.Empty : (dict["data"][i]["title"]);
edata.dateRecordStarted = Convert.ToDateTime((dict["data"][i]["dateRecordStart"]));
edata.DateAPIRan = DateTime.Now;
if (CheckAutoTag(checkId, dict["data"][i]["tags"]))
{
edata.AutoTagged = true;
}
else edata.AutoTagged = false;
arrData.Add(edata);

edata is a reference type. You keep updating the values of a single object within the loop.
You need to call new eSummary() and set the values on the new object and then add that to your list.
But do note, you should not be using ArrayList in modern c#. Use a List<eSummary> instead.

Related

HtmlAgilityPack - Parse table and assign rows to custom model

So I'm trying to scrape some website data (specifically the first table here). I am using the table xpath, and trying to get the specific row data assigned to my model.
public static async Task<List<SuspensionModel>> GetSuspensionData()
{
var htmlDocument = new HtmlDocument();
var httpResponseMessage = await _httpClient.GetAsync(_2020SuspUrl);
await EnsureSuccessStatusCode(httpResponseMessage);
var SuspStatsAsHtml = await httpResponseMessage.Content.ReadAsStringAsync();
htmlDocument.LoadHtml(SuspStatsAsHtml);
var suspData = ParseTable(htmlDocument, "/html/body/div[3]/div[3]/div[5]/div[1]/table[1]/tbody/tr");
//return ;
}
private static List<SuspensionModel> ParseTable(HtmlDocument htmlDocument, string xPath)
{
var returnData = new List<SuspensionModel>();
foreach (HtmlNode row in htmlDocument.DocumentNode.SelectNodes(xPath))
{
HtmlNodeCollection cells = row.SelectNodes("td");
var arr = new String[7];
for (int i = 0; i < cells.Count; ++i)
{
arr[i] = cells[i].InnerText;
}
var susp = new SuspensionModel
{
IncidentDate = DateTime.Parse(arr[0]),
OffenderName = arr[1],
OffenderTeam = arr[2],
OffenseDesc = arr[3],
ActionDate = DateTime.Parse(arr[4]),
OffenseLength = arr[5],
SalaryLoss = int.Parse(arr[6])
};
returnData.Add(susp);
}
return returnData;
}
In my ParseTable method, where I am assigning values in my model, how can I access the specific cell data in the given row? Basically, I want to do something like:
foreach row, step through each cell and assign to the correct model value. As I have it now, my cells variable always returns null, so I assume I am not using HtmlAgilityPack correctly.
Any assistance is appreciated here!
I ended up resolving this. I was missing two things, and it turns out it wasn't related to HtmlAgilityPack.
I needed to add .Skip(1) to my foreach row so that it skipped the table header row.
foreach (HtmlNode row in htmlDocument.DocumentNode.SelectNodes(xPath).Skip(1))
I needed to fix my SalaryLoss value. I was assigning it as an int, but I needed to change that to a double as it was a currency value.
SalaryLoss = double.Parse(arr[6], System.Globalization.NumberStyles.Currency)

C# GoogleSheets Update Request not Executing

I'm trying to update values in a Google Spreadsheet, The code executes up to the addRequest.Execute(); however, it does not run the execute statement.
This does work if i run a Append Request, however im not trying to append, im trying to update.
I have the following Scopes for the program static readonly string[] Scope = { SheetsService.Scope.Spreadsheets, DriveService.Scope.Drive};
var range = $"{ClashImport[i][0].ToString()}!B7:F106";
var REALInsertList = new sData.ValueRange();
var InsertList = new List<object>();
for (int n = 0; n < DataImport[i].Count; n++) {
InsertList.Add(DataImport[i][n].AccountName);
InsertList.Add(DataImport[i][n].AccountID);
InsertList.Add(DataImport[i][n].Banned);
InsertList.Add(DataImport[i][n].Suspended);
InsertList.Add(DataImport[i][n].History);
}
REALInsertList.Values = new List<IList<object>> { InsertList };
var addRequest = sheetsService.Spreadsheets.Values.Update(REALInsertList, SheetToImportTo, range);
addRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.USERENTERED;
addRequest.Execute();
This example will help you to achieve what you are trying to do:
// Define request parameters.
// The ID of the spreadsheet to update.
string spreadsheetId = "YOUR-SPREADSHEET-ID"; // TODO: Update placeholder value.
// How the input data should be interpreted.
string valueInputOption = "RAW"; // TODO: Update placeholder value. Ex -> RAW
// The new values to apply to the spreadsheet.
List<ValueRange> data = new List<ValueRange>(); // Instanciate a list of type ValueRange
ValueRange values = new ValueRange(); // Instanciate a ValueRange object
values.Range = "A1:B2"; // The range you want to update
// Depending in your number of rows, create some logic to populate them
List<object> firstRow = new List<object> { "Hello", 2};
List<object> secondRow = new List<object> { 3, "Hey!"};
// Populate the values to be inserted in the sheet
values.Values = new List<IList<object>> { firstRow, secondRow };
// add values to the data ValueRange List
data.Add(values);
// TODO: Assign values to desired properties of `requestBody`:
BatchUpdateValuesRequest requestBody = new BatchUpdateValuesRequest();
requestBody.ValueInputOption = valueInputOption;
requestBody.IncludeValuesInResponse = true;
requestBody.Data = data;
// Build and make the request
SpreadsheetsResource.ValuesResource.BatchUpdateRequest request
= service.Spreadsheets.Values.BatchUpdate(requestBody, spreadsheetId);
BatchUpdateValuesResponse response = request.Execute();
IList<IList<object>> updatedValues = response.Responses[0].UpdatedData.Values;
// Print updated values
Console.WriteLine("These are the updated values");
foreach (var row in updatedValues)
{
Console.WriteLine("{0}, {1}", row[0], row[1]);
}
Console.Read();
Following the Method: spreadsheets.values.batchUpdate endpoint documentation and the Try this API, I was able to get an idea on how to build the request update body.
Notice I created a List<ValueRange>, which I populate with the appropriate data and in that way make the request.
Documentation
For more info, you can check:
.NET Quickstart
Google Sheets API .NET reference documentation

Update list item multiple lookup value field Microsoft Graph

I am trying to update a multiple lookup value field for a list item.
I tried the following code :
List < QueryOption > options = new List < QueryOption > {
new QueryOption("$expand", "listitem")
};
//get drive item with list item
var driveItem = graphClient.Sites[IdGestDoc].Drive.Items[itemResult.Id].Request(options).GetAsync().Result;
var fieldValueSet = new FieldValueSet {
AdditionalData = new Dictionary < string,
object > {
{
"Theme_fonctionnel#odata.type",
"Collection(Edm.String)"
}, {
"Theme_fonctionnel", ThemeFonctionnel.ToArray()
} //ThemeFonctionnel is a List<string> => lookupid
}
};
await graphClient.Sites[IdGestDoc].Lists["Documents"].Items[driveItem.ListItem.Id].Fields.Request().UpdateAsync(fieldValueSet);
But this code don't work and I don't find what I am missing.
Any help will be appreciated !
To set a Lookup field, you need to set the property by passing in the property name and the addition 'LookupId':
string propertyName = "Theme_fonctionnel";
var fieldValueSet = new FieldValueSet();
var propertyValuesArray = options.ToArray();
var attributes = new Dictionary<string, object>();
//first, we need to specify the input data type
string oDataTypeInfoPropertyName = propertyName + "LookupId#odata.type";
string oDataDataType = "Collection(Edm.String)";
attributes.Add(oDataTypeInfoPropertyName, oDataDataType);
//next, we need to pass the values as an array
string newPropertyName = propertyName + "LookupId";
attributes.Add(newPropertyName, propertyValuesArray);
fieldValueSet.AdditionalData = attributes;

Adding data to datagridview without losing existing rows

hello every body i am new to c# and now im trying to add rows to datagridview using query and invoke but the problem is whenever i try to insert data to table in rewrites all rows so just one row that has the last verified value shown in the rows. i have tried so many thing but i dont know where the problem is. this is my code:
XDocument xdoc = XDocument.Load("demo.xml");
var query = from key in xdoc.Descendants("user")
where key != null && (key.Element("clientno").Value == recieveddata)
select new
{
//listBox3.Items.Add(key.Element("name")+""+key.Element("lastname"));
نام = key.Element("name").Value +" "+ key.Element("lastname").Value,
// خانوادگی = key.Element("lastname").Value,
ورزش = key.Element("noeozviat").Value,
تاریخ = key.Element("date").Value,
عضویت = key.Element("duration").Value,
جلسات = key.Element("jalasat").Value
};dataGridView1.Invoke(new Action(() => dataGridView1.DataSource = query.ToList()));
please help me on this.
You are creating a new list and setting it as the data source every time. What you should be doing is creating a class for each element (at the moment you are using an anonymous type), then ADD new elements when you run the query and set this list as the new data source.
Something like this:
class Thing
{
public string نام {get;set;}
// Etc
}
List<Thing> items = new List<Thing>(); // <- should be a class member variable, not a local variable
XDocument xdoc = XDocument.Load("demo.xml");
var newItems = from key in xdoc.Descendants("user")
where key != null && (key.Element("clientno").Value == recieveddata)
select new Thing
{
نام = key.Element("name").Value +" "+ key.Element("lastname").Value,
ورزش = key.Element("noeozviat").Value,
تاریخ = key.Element("date").Value,
عضویت = key.Element("duration").Value,
جلسات = key.Element("jalasat").Value
};
items.AddRange(newItems.ToList());
dataGridView1.Invoke(new Action(() => dataGridView1.DataSource = items));

How can I get the value of 1 list depending on the value of another list

I'm trying to accomplish the following and would appreciate any help.
I have a CSV file containing 2 columns, 1 a list of names and the other a list of associated IDs.
Eg.
Test1, 00001
Test2, 00002
I have read these into my program into 2 lists.
1. nameList
2. idList
I have populated the datasource of a combobox with the values of nameList.
Now, when a name is selected in the combobox and a button pressed, I would like to get the ID. So if Test1 is selected, when the button is pressed 00001 is returned where as if Test2 is selected and the button pressed, 00002 is returned.
If it helps at all, this is what im currently using to populate the Lists.
public void nameCSV()
{
var reader = new StreamReader(File.OpenRead(#"C:\temp\nameList.csv"));
List<string> nameList = new List<string>();
List<string> idList = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
nameList.Add(values[0]);
idList.Add(values[1]);
}
cmbxName.DataSource = releaseNameList;
}
public void nameCSV()
{
var reader = new StreamReader(File.OpenRead(#"C:\temp\nameList.csv"));
DataTable tbl = new DataTable();
tbl.Columns.Add("Name", typeof(string));
tbl.Columns.Add("ID", typeof(string));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
DataRow row = tbl.NewRow();
row["Name"]=values[0];
row["ID"] = values[1];
tbl.Rows.Add(row);
}
cmbxName.DisplayMember = "Name";
cmbxName.ValueMember = "ID";
cmbxName.DataSource = tbl;
}
You need something like that. Check properties DisplayMember and ValueMember of ComboBox.
So when the button is clicked and you want to take the Value not the display text.
protected void Button1_Click(object sender, EventArgs e)
{
string id = cmbxName.SelectedValue;
}
N.B: Your title is misleading !
Use a Dictionary
public void nameCSV()
{
var reader = new StreamReader(File.OpenRead(#"C:\temp\nameList.csv"));
Dictionary<string, int> userDict = new Dictionary<string, int>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
userDict.Add(values[0], values[1]);
}
cmbxName.DataSource = userDict;
}
to call the value of a given user id just use the key. For example, 'Test1'
string Username = "Test1";
int userid;
userid = userDict[Username];
One note, dictionaries work better with numeric values as the key, not strings. Put in your case you're looking up the string name, so it should be this way. As long as the string lengths are short - aka no more than 10 characters. Peformance wise, you should be okay.

Categories