c#, Get index of current T in List<T> - c#

I searched quite a bit, i might have Stumbled upon something simmilar but it's stil unlear to me as im new to c#.
i want to get current index of the object Tline from a List< Tline >
i know i can simply iterate with an int i. but i dont understand why or how i can get it as indexof current iteam i'm possitioned on without searching for anything.
Tline is something like
public class Tline
{
public string Cd_m { get; set; }
public string cd_d { get; set; }
public string cd_c { get; set; }
...
}
My Issue is here(indicated with arrows)
class ACCS
{
internal void DBwrite(List<Tline> imoprtati)
{
OleDbConnection myAccessConn = null;
string sFld = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string dbName = "EDIM1.mdb";
dbName = Path.Combine(sFld, dbName);
string accessConn = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}", dbName);
string sqlQuery = "INSERT INTO DOTes (`id`,`cd_m`,`cd_d`,`cd_c`,`nume`) values (?,?,?,?,?)";
myAccessConn = new OleDbConnection(accessConn);
int i = 2;
foreach (var insT in imoprtati)
{
using (OleDbCommand cmd = new OleDbCommand(sqlQuery, myAccessConn))
{
cmd.Connection.Open();
cmd.Parameters.AddWithValue("#id", i.ToString());
cmd.Parameters.AddWithValue("#cd_m", "2018");
cmd.Parameters.AddWithValue("#cd_d", "BO");
cmd.Parameters.AddWithValue("#cd_c", "C00128");
// Show current Tline Index ↓↓↓↓↓↓↓
cmd.Parameters.AddWithValue("#nume", (imoprtati.GetEnumerator().Current)); //trying anything
cmd.ExecuteNonQuery();
cmd.Connection.Close();
i++;
}
}
}
}

imoprtati.GetEnumerator().Current won't work, because you are creating a separate iterator with it's own position (before the first item currently). Your best bet here is probably a simple counter:
int index = 0;
foreach (var insT in imoprtati) {
// your stuff here...
index++;
}
There is a LINQ method that includes the index, but that will end up being less efficient due to custom struct iterators, captured variables, etc (complex topic).

List.GetEnumerator().Current will create a new enumerator for the list and then return the current item, not index. This will not work as you have seen.
As List implements IList, you can use the IndexOf(T item); method to find the 0 based index of an item.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1.indexof?view=netframework-4.7.2#System_Collections_Generic_IList_1_IndexOf__0_
However, this method does not guarantee constant O(1) performance as it must search through the list. A better alternative would be to record the index as you iterate through them as you have stated.
There is no way to find the index of an arbitrary item in a list in constant time without recording the index, so that is what I would recommend.

Related

How do I append the contents of a txt file into an array, but the txt file has different variable types

I'm not that great with coding and all since we've started learning the basics this year, and my groupmates aren't doing anything to help me, so I'm lost here and wanted to seek help from all the experts that are here.
I was wondering how I'd be able to sort the mixed data inside the Textbox properly
(I'm using the console.application to access windows.Forms because that's what our professor wants us to do) . As you can see in the image below, it gets arranged alphabetically instead of sorting the score in descending order with the names of the player beside their scores.
void show_Click(object sender, EventArgs e)
{
int counter = 0;
string line;
StreamReader TXT = new StreamReader("Leaderboard.txt");
List<string> list = new List<string>();
while ((line = TXT.ReadLine()) != null)
{
ListB1.Items.Add(line);
list.Add(line);
counter++;
}
string[] arr = list.ToArray();
Array.Sort(arr);
Array.Reverse(arr);
foreach (string item in arr)
{
ListB2.Items.Add(item);
}
this is barely a fraction of my whole code but this is the part I think is most essential to making my leaderboards function properly.
this is what my code results to, as of now...
Currently you're reading each line of the file as a single string. So there's no difference between "score" and "text", it's all just one text value.
It sounds like what you want is to parse that information into an object with two different properties. For example, suppose you have this class:
public class ScoreListing
{
public int Score { get; set; }
public string Text { get; set; }
}
Then your list would be something like:
List<ScoreListing> list = new List<ScoreListing>();
And to add to it, you'd need to parse information from line to create a new ScoreListing object. For example, it might look something like this:
while ((line = TXT.ReadLine()) != null)
{
var elements = line.Split(' | ');
var listing = new ScoreListing
{
Score = int.Parse(elements[0]),
Text = elements[1]
};
list.Add(listing);
counter++;
}
Once you have a populated List<ScoreListing> you can sort it with .OrderBy(), for example:
var sortedLeaderboard = list.OrderByDescending(l => l.Score);
There are multiple ways to approach the storage of the data, sorting the data, etc. But overall when you have a value like "123 | SomeText" then what you really have is a custom-serialized object with two distinct properties. Your first goal when reading this data into memory should be to de-serialize it into that meaningful object. Then you can easily use that object however you like.
By request, a bit more information on converting data back into a string...
The most object-oriented approach would be to override .ToString() on the class:
public class ScoreListing
{
public int Score { get; set; }
public string Text { get; set; }
public override string ToString()
{
return $"{Score} | {Text}";
}
}
Then you can add it to your list of strings:
foreach (var item in sortedLeaderboard)
{
ListB2.Items.Add(item.ToString());
}
Without overriding .ToString() you'd just manually perform the same operation:
foreach (var item in sortedLeaderboard)
{
ListB2.Items.Add($"{item.Score} | {item.Text}");
}

ASP.NET SqlDataReader and Lists

I am getting data via a SqlDataReader and now looping through the results and putting the results in the list. I am trying to add 2 columns to each list, but I am unsuccessful.
Here is my code:
for (int i = 0; i < reader.FieldCount; i++)
{
List<string> costs = new List<string>();
if (reader.GetName(i).ToString().Contains("TotalCost"))
{
costs.Add(reader.GetValue(i).ToString());
}
if (reader.GetName(i).ToString().Contains("SqftCost"))
{
costs.Add(reader.GetValue(i).ToString());
}
jobList.Add(costs);
}
But this puts the two columns in separate lists, I really need the 2 columns in one list.
The reason I am doing it like this is because I have columns that are called TotalCost101, SqftCost101, TotalCost102, SqftCost102, TotalCost104, SqftCost104. So each column that contains TotalCost and SqftCost should be in its own list. I hope this makes sense, anyone got any ideas on how to put these 2 columns in their own list. So at the end I will have a bunch of lists with 2 values.
I updated my code so I now use a Class instead of a List
for (int i = 0; i < reader.FieldCount; i++)
{
CostMatrix costs = new CostMatrix();
if (reader.GetName(i).ToString().Contains("TotalCost"))
{
costs.TotalCost = reader.GetValue(i).ToString();
}
if (reader.GetName(i).ToString().Contains("SqftCost"))
{
costs.sqftCost = reader.GetValue(i).ToString();
}
jobList.Add(costs);
}
Here is the current output:
<d3p1:CostMatrix>
<d3p1:TotalCost>550</d3p1:TotalCost>
<d3p1:sqftCost i:nil="true"/>
</d3p1:CostMatrix>
<d3p1:CostMatrix>
<d3p1:TotalCost i:nil="true"/>
<d3p1:sqftCost>0.41</d3p1:sqftCost>
</d3p1:CostMatrix>
What I am looking for is:
<d3p1:CostMatrix>
<d3p1:TotalCost>550</d3p1:TotalCost>
<d3p1:sqftCost>0.41</d3p1:sqftCost>
</d3p1:CostMatrix>
Honestly, I would use an object.
public class Price
{
public decimal Sqft { get; set; }
public decimal Total { get; set; }
}
You have an object that actually represents something tangible. You're clearly indicating what type of price is applicable. This will avoid confusion with other people working on the project and for you, with an expressive usage. Nothing is being obfuscated into a Tuple or string.
Then when you use the data reader, you could do something along these lines:
public static T GetValueOrNull<T>(this IDataReader reader, string column)
{
int ordinal;
if(!string.IsNullOrEmpty(column) && !reader.IsDBNull(reader.GetOrdinal(column)))
if(int.TryParse(reader.GetOrdinal(column).ToString(), out ordinal))
return (T)reader.GetValue(ordinal);
return default(T);
}
You can basically tell this, "which column" then assign it to that property. This could also be handled by some form of object relational mapper.
// Example:
List<Product> products = db.GetProducts();
var example = products.Where(o => o.Price.Sqft >= 5.00);
var sample = products.Where(o => o.Price.Total <= 5.00);
You can store Price inside of another object, allowing a web-site to filter a product based on how multiple types of price values, for instance. It has other benefits as well, it will also document your code nicely, to know how pricing may be implemented.
Not search for a collection of strings, how would that persist throughout your application? A List<string> would be hard to find all implementations for price, unless seeking a data attribute. But these are a bunch of reasons.
Hope this clarifies a bit.
Based on your update, you could do:
public class CostMatrix
{
public ConcurrentList<decimal> Total { get; set; }
public ConcurrentList<decimal> Sqft {get; set; }
}
Your object would have two separate list, then as you read through the table column by column and row by row, you could simply add. So if you used the above static method it would be:
using(var connection = new SqlConnection(dbConnection))
using(var command = new SqlCommand(query, dbConnection))
using(var reader = new SqlDataReader())
while(reader.Read())
{
Total.Add(GetValueOrNull<decimal>(reader, "TotalCost");
Sqft.Add(GetValueOrNull<decimal>(reader, "Sqft1");
}
I placed ConcurrentList because your implementation may use async. Wasn't sure, if not you can use a normal List. But a List isn't thread safe by default, you'd need a ConcurrentList.

printing objects with a foreach loop and NullReferenceException [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 8 years ago.
I've started learning C# and I have been following a few "mini projects" I found on the net and some I made up my self to help me understand the basics. This mini project requires me to create two classes that are named "item" and "inventory". The idea is that the item class is used to create items and the other inventory class is used to store the items and print them all. Here's the code so far:
class Program
{
static void Main(string[] args)
{
inventory my_inventory = new inventory();
item cake = new item("Cake", 2.99, 001);
item carrot = new item("Carrot", 0.59, 002);
my_inventory.add_item(cake);
my_inventory.add_item(carrot);
my_inventory.print_inv();
Console.ReadLine();
}
}
class item
{
string name;
double price;
int id;
public item (string Name, double Price, int ID)
{
this.name = Name;
this.price = Price;
this.id = ID;
}
public item()
{
this.name = "unknown";
this.price = 0.00;
this.id = 000;
}
public override string ToString()
{
return "Name: " + name + " Price: " + price + " ID Number: " + id;
}
}
class inventory
{
object[] inv_list = new object[10];
int tracker = 0;
public void add_item(object obj)
{
inv_list[tracker] = obj;
tracker++;
}
public void print_inv()
{
foreach ( object obj in inv_list) { Console.WriteLine(obj.ToString()); }
}
}
The error I keep running into is the "NullReferenceException" inside the print_inv() method and from what I have read it means that the object I'm trying to use on the print_inv() method is null? I'm not sure what this means in my code.
The thing here is that when you create an array of something it's initialized with the default value for something. In case of object the default value is null.
So you need to modify you print_inv method to look through existing items:
public void print_inv()
{
for(int i =0; i < tracker; i++)
{
Console.WriteLine(inv_list[i].ToString());
}
}
The issue is that since your declaring an array of a specific size (new object[10]) th earray is always that size. Therefore, when you iterate over it (foreach(object obj in inv_list) you're going to get everything, not just the values you've explicitly initialized. Since the default of object is null, then all but those explicit items out of your array are null.
There are a couple ways to fix this:
Replace foreach with for(int i = 0; i < tracker; i++) - this will only iterate through the items up to the tracker count, and no more.
Use a List<object> instead of an array. This will allow you to add/remove items without having to worry about capacity explicitly, and thus should avoid most auto-initialized values. May require more code to keep the inventory under 10 items, though.
Check for null and continue or break when you hit a null item in the inventory.

What is the most effective way to 'align' two separate lists by ordinal in a single ItemsControl?

Over-simplifying our model for the purposes of this example, let's say we have two lists of data, ListA and ListB, both of which are of type List<string>. From a data perspective, they are not related. ListA and ListB can be added to, removed from, or otherwise updated independently.
What we're trying to do is display them both at the same time in the same list, aligned by ordinal position.
Our first approach was to create a new ListMapping object as follows:
public class ListMapping
{
public int Index{ get; set; }
public string StringA{ get; set; }
public string StringB{ get; set; }
}
then create a List<ListMapping> relating the strings at ordinal position 'x' of ListA and ListB and we'd initialize it like this:
var MappedList = new List<ListMapping>();
var maxItems = Math.Max(ListA.Count, ListB.Count);
for(int index = 0; index < maxItems; index++)
{
var lm = new ListMapping(){
Index = index,
StringA = (index < ListA.Count) ? ListA[index] : null;
StringB = (index < ListB.Count) ? ListB[index] : null;
}
MappedList.Add(lm);
}
The problem with this approach is we had to manually manage this new list of ListMap objects. If an item is deleted from ListB, then we need to manually shift all the ListMapping.StringB properties up one position to 'realign' with the new ListMapping.StringA. Same thing with Insert, etc.
Our next approach was to not actually store the string values in ListMapping, just the index, and make the getters return the value directly from the underlying lists, like this...
public class ListMapping
{
public int Index{ get; set; }
public string StringA{ get{ (Index < ListA.Count) ? ListA[Index] : null; } }
public string StringB{ get{ (Index < ListB.Count) ? ListB[Index] : null; } }
}
And then we'd initialize the List<ListMapping> object like this...
var MappedList = new List<ListMapping>();
var maxItems = Math.Max(ListA.Count, ListB.Count);
for(int index = 0; index < maxItems; index++)
{
var lm = new ListMapping(){
Index = index
}
MappedList.Add(lm);
}
Using this design, we'd simply need to trigger property changed notifications for the StringA and StringB properties of any ListMapping with an index that would have been affected by an operation on either ListA or ListB. Definitely cleaner and no held references to the source objects, but now they had to have a reference to the List objects themselves. Plus, we still need to manually manage the overall list of ListMapping items to ensure there's at least 'maxItems' items at all times. Better, but not ideal.
I can't help but wonder if there's a way to construct an ItemsControl to have two ItemsSource properties then do something clever with its layout panel and ItemContainerGenerator, but that just seems like I'd be doing in the UI what I'm already doing in the data.
So, any thoughts on a way to solve this issue?

problem in preparing where clause of an update method in c#

hey guys, i'm finding little difficult to prepare a where clause in following update method
i'm creating a update method, here am i doin' this in a rightway ? but still m confused that how do i know that which property is to use in where clause
or any better approach to create a generic update method ? i wud be very thankfull
EDIT
public bool UpdateData(object Entity, ref String error)
{
Type objectType = Entity.GetType();
PropertyInfo[] properties = objectType.GetProperties();
error = "";
string column = null;
int i = 0;
SqlConnection conn = OpenConnection();
SqlCommand sqlcommand=null;
foreach (PropertyInfo info in properties)
{
if (i == 0)
{
i++;
continue;
}
column += (i >= 0 && i < properties.Length - 1) ? string.Format(#"{0}=#{0},", info.Name) : string.Format(#"{0}=#{0}",info.Name);
i++;
}
try
{
string sqlQuery = string.Format(#"update {0} set {1}
where {2}='{3}'", objectType.Name, column,1,1);//see here m not getting how to prepare this where clause
sqlcommand = new SqlCommand(sqlQuery, conn);
i = 0;
foreach (PropertyInfo info in properties)
{
if (i == 0)
{
i++;
continue;
}
sqlcommand.Parameters.AddWithValue(string.Format("#{0}", info.Name), info.GetValue(Entity, null));
}
sqlcommand.ExecuteNonQuery();
sqlcommand = null;
return true;
}
catch (Exception ex)
{
return false;
}
finally
{
CloseConnection(conn);
}
}
EDIT
see in above code there is an if condition if (i == 0) in foreach loop i dont want to do this way coz here i'm assuming my objects property i.e employee_id is at 1st index in array of PropertyInfo[].. what if some one makes a class properties like public string employee_name{get,set} public string employee_add{get,set} public string employee_id{get,set} in this case the foreach loop will skip employee_name instead of 'employee_id' coz i used if(i=0), i want a way to skip only identity value i.e employee_id in foreach loop in my update function irresptive of its index in propertyInfo array..... did i explained well ?
You can use a custom attribute or an interface to determine the property that represent the id of your entity and use it in your where clause. Although this approach has a negative side that you have to change your entity classes (which is not always possible).
Here's an example :
[PrimaryKey]
public string EmployeeId
{
get;set;
}
or
public class Employee:IEntity
{
public object EntityId
{
get
{
return this.EmployeeId
}
}
}
another way might be to store the entities metadata somewhere else (a xml file or a dictionary for example)
for example:
<entities>
<entity type="Employee" primarykey="EmployeeId" />
</entities>
or
Hashtable EntityPrimaryKeys=new Hashtable{(typeof(Employee),"EmployeeId")};
You really should use SQLCOmmand.Parameters instead of concatenating strings int he sql.
That way it will be more readable, secure and more feature proof.
You have a classical query exploit in your code where somebody could send in a parameter containing '; drop database yourdatabase; select * from dual where ''=' or similar.
So please update your code and if you have the same issue we will see what's going on.

Categories