I have some problems with collection lists. I use the list for saving some customer data which i collect from xml and textfiles.
// First i create an instance of the list
List<Customer> cusList = new List<Customer>();
// Save files
String[] somefiles = Directory.GetFiles(//FromPath");
// Then i loop through some files and collect data for the list
for (int i=0; i<somefiles.length; i++)
{
if (some statements match)
{
// call a methode and save file data to the cuslist
cuslist= callmethode(somefiles);
}
else
{
System.Console.WriteLine("Do nothing");
}
}
I want to extend the list for all files, but at the moment i get after the loop only data from the last file.
How can i handle it, that it saves all the data from all files?
Kind regards
When you write cuslist= callmethode(file); you re-assign the list at every iteration. What you need instead is something like this:
cuslist.AddRange(callmethode(file));
This will just add the elements returned from the method to your list instead of replacing the entire list.
If the method just returns one single element use cuslist.Add instead.
HimBromBeere explained the issue and provided the correct answer, just as a side-note:
You could use LINQ to simplify this task:
List<Customer> cusList = somefiles
.Where(f => some statements match)
.SelectMany(f => callmethode(f))
.ToList();
Related
Hi i have this code To export a List to An Excel:
private DataTable ListaDatiReportQuietanzamento(List<DatiReportQuietanzamento> datiReportQuietanzamento)
{
DataTable dt = new DataTable("DatiReportQuietanzamento");
dt.Columns.Add("Polizza");
dt.Columns.Add("Posizione");
dt.Columns.Add("Codice Frazionamento");
var result = datiReportQuietanzamento.ToDataTable().AsEnumerable().Select(p =>
new
{
n_polizza = p.Field<long>("n_polizza"),
n_posizione = p.Field<byte>("n_posizione"),
c_frazionamento = p.Field<string>("c_frazionamento")
}).Distinct().ToList();
foreach (var item in result)
{
dt.Rows.Add(item.n_polizza, item.n_posizione, item.c_frazionamento);
}
return dt;
}
This method works with Lists that does not contain many items , but when the list is very large , the method takes too many time.
There is a way to avoid the foreach and add to the rows the items directly? Maybe with Lambda Expression?
Thank you.
While you have not specified how the data is ultimately to be supplied to Excel, generally it is supplied a CSV (Comma Separated Values) file for easy import.
So this being the case you can eliminate your data table conversion entirely and create a list of strings as follows:
private List<string> ListaDatiReportQuietanzamento(List<DatiReportQuietanzamento> datiReportQuietanzamento)
{
var result = new List<string>();
foreach (var item in datiReportQuietanzamento)
{
result.AppendLine($"{item.n_polizza},{item.n_posizione},{item.c_frazionamento}");
}
return result;
}
Now the only simplification I have made is not to worry about encoding because strings should actually be escaped so item.c_frazionamento should actually be escaped.
Instead of doing this all yourself, I suggest you have a look at a NuGet package such as CsvHelper which will help you with creating CSV files and take all the hassle with escaping things out of the equation. It can also directly deal with a list of objects and convert it into a CSV file for you see specifically the first example in https://joshclose.github.io/CsvHelper/writing#writing-all-records
I have a list of objects which I sort multiple times throughout code and when the user interacts with the program. I was wondering if it would be better to insert new items into the list rather than add to the end of the list and resort the entire list.
The code below is for importing browser bookmarks - Here I add a bunch of bookmarks to the List (this._MyLinks) which are Link objects and then sort the final List - Which I think is probably best in this given scenario....
public void ImportBookmarks(string importFile)
{
using (var file = File.OpenRead(importFile))
{
var reader = new NetscapeBookmarksReader();
var bookmarks = reader.Read(file);
foreach (var b in bookmarks.AllLinks)
{
bool duplicate = this._MyLinks.Any(link => link._URL == b.Url);
if(duplicate)
{
continue;
}
Link bookmark = new Link();
bookmark._URL = b.Url;
bookmark._SiteName = b.Title;
bookmark.BrowserPath = "";
bookmark.BrowserName = "";
if (bookmark.AddToConfig(true))
{
this._MyLinks.Add(bookmark);
}
}
}
this._MyLinks = this._MyLinks.OrderBy(o => o._SiteName).ToList();
}
Now a user also has the option to add their own links (one at a time). Whenever the user adds a link the ENTIRE list is sorted again using
this._MyLinks = this._MyLinks.OrderBy(o => o._SiteName).ToList();
Is it better from a preformance standpoint (or just generally) to just insert the item directly into it's specified location? If so would you have suggestions on how I can go about doing that?
Thanks!
Since you want a sorted set of data you should be using a more appropriate data structure, specifically a sorted data structure, rather than using an unsorted data structure that you re-sort every time, or that forces you to inefficiently add items to the middle of a list.
SortedSet is specifically designed to maintain a sorted set of data efficiently.
I am reading in lines from a large text file. Amongst these file are occasional strings, which are in a preset list of possibilities, and I wish to check the line currently being read for a match to any of the strings in the possibilities list. If there is a match I want to simply append them to a different list, and continue the loop I am using to read the file.
I was just wondering if there is a more efficent way to do a line.Contains() or equivilance check against say the first element in the list, then the second, etc. without using a nested loop or a long if statement filled with "or"s.
Example of what I have now:
List<string> possible = new List<string> {"Cat", "Dog"}
using(StreamReader sr = new StreamReader(someFile))
{
string aLine;
while ((aLine = sr.Readline()) != null)
{
if (...)
{
foreach (string element in possible)
{
if line.Contains(element) == true
{
~add to some other list
continue
}
}
~other stuff
}
}
I don't know about more efficient run-time wise, but you can eliminate a lot of code by using LINQ:
otherList.AddRange(File.ReadAllLines(somefile).
.Where(line => possible.Any(p => line.Contains(p)));
I guess you are looking for:
if(possible.Any(r=> line.Contains(r)))
{
}
You can separate your work to Get Data and then Analyse Data. You don't have to do it in the same loop.
After reading lines, there are many ways to filter them. The most readable and maintenable IMO is to use Linq.
You can change your code to this:
// get lines
var lines = File.ReadLines("someFile");
// what I am looking for
var clues = new List<string> { "Cat", "Dog" };
// filter 1. Are there clues? This is if you only want to know
var haveCluesInLines = lines.Any(l => clues.Any(c => l.Contains(c)));
// filter 2. Get lines with clues
var linesWithClues = lines.Where(l => clues.Any(c => l.Contains(c)));
Edit:
Most likely you will have little clues and many lines. This example checks each line with every clue, saving time.
I am fairly new to C#
I am trying to retrieve some information from an external data source and store it in array, once it is in an array I wish to sort it by time.
I know how to do this for just one column in a row, however the information I require has multiple columns.
For example:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
// Sort my array by Appoint.Start
foreach ( item in myNewArray )
{
//print out Appoint.Subject - Appoint.Start, Appoint.Organiser.Name.ToString() and Appoint.location
}
Many thanks for your help.
EDIT:
I have multiple data sources which pull in this:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
Hence the need to sort the items in a new array, I know this isn't very efficent but there is no way of getting the information I need in any other way.
You can sort a list using the LINQ sorting operators OrderBy and ThenBy, as shown below.
using System.Linq;
and then...
var appointments = new List<Appointment>();
var sortedAppointments = list.OrderBy(l => l.Subject).ThenBy(l => l.Name).ToList();
This will create a new list of appointments, sorted by subject and then by name.
It's unclear what your final aim is but:
Use a generic List instead of an array:
See this SO question for more information as to why using a List is prefered.
List<Appointment> appointments = new List<Appointment>();
foreach (Appointment Appoint in fapts)
{
appointments.Add(Appoint);
}
foreach (var item in appointments)
{
Console.WriteLine(item.Subject);
Console.WriteLine(item.Foo);
// Here you could override ToString() on Appointment to print eveything in one Console.WriteLine
}
If the aim of your code is to order by time, try the following:
var sortedAppointments = fapts.OrderBy(a => a.Start); // assuming Start is a DateTime property of `Appointment`.
Consider a Dictionary Object instead of an array if the data is conceptually one row multiple columns.
foreach(KeyValuePair<string, string> entry in MyDic)
{
// do something with entry.Value or entry.Key
}
You already have a list of objects in fpts, sort that list itself:
fpts.OrderBy(x => x.Subject).ThenBy(x => x.Location).ToList();
LINQ is your friend here.
fapts appears to already be a collection so you could just operate on it.
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start).ToArray()
I've used the ToArray() call to force immediate evaluation and means that myNewArray is already sorted so that if you use it more than once you don't have to re-evaluate the sort.
Alternatively if you are only using this once you can just as easily miss the ToArray() portion out and then execution of the sort will be deferred until you try and enumerate through myNewArray.
This solution puts the source objects into the array, but if you are just wanting to store the specific fields you mention then you will need to use a select. You have two choices for the array item type, you can either use an anonymous class which provides difficulties if you are returning this array from a function or define a class.
For anonymous:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
For named class assuming class is MyClass:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new MyClass {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
You have a wide range of options. The 2 most common are:
1) Create a class, then define an array or list of that class, and populate that
2) Create a structure that matches the data format and create an array or list of that
Of course, you could put the data into an XML format or dataset, but that's probably more work than you need.
public List<foo> appointments = new List<foo>();
public struct foo
{
public string subject ;
public DateTime start ;
public string name ;
public string location ;
}
public void foo1()
{
// parse the file
while (!File.eof())
{
// Read the next line...
var myRecord = new foo() ;
myRecord.subject = data.subject ;
myRecord.start = data.Start ;
myRecord.name = data.Name ;
//...
appointments.Add(myRecord);
}
}
Enjoy
(Since I can't comment and reply to the comment - it wasn't clear if he had a class, etc. or was just showing us what he wanted to do. I assumed it was just for demonstration purposes since there wasn't any info as to how the data was being read. If he could already put it into a class, than the first answer applied anyway. I just tossed the last 2 in there because they were options for getting the data first.)
I have a List of string values and and some of the values contains xxx or XXX in front.
xxxRed
xxxYellow
xxxxCareful with that axe Eugene!
xxxxxxdedicum aceasta frumoasa Melodia
xxxxLeaders
xxxxWorking Around - titles
XXXXXNothing To Fear
xxxxAvoiding standards
xxxFirst Aid
List<string> lstTitles = new List<string>();
This is what I have tried
for (int i=0; i < lstTitles.Count; i++)
{
string title = lstTitles[i].ToLower().Trim();
if (title[0] == 'x')
{
lstTitles.Remove(lstTitles[i]);
}
}
Problem I have is that only some of the values are removed but not all of them.
Is there perhaps a better way of removing these values?
Use RemoveAll method
lstTitles.RemoveAll(s => s[0] == 'x' || s[0] == 'X');
and you may want to use StartsWith instead of comparing first char.
lstTitles.RemoveAll(s => s.StartsWith("x",StringComparison.InvariantCultureIgnoreCase));
Problem I have is that Only some of the values are removed but not all of them.
Because you're skipping items. When you call Remove(), the next item will be at index i, but you'll increase i in the next loop.
It can be solved by iterating over a copy of the list, and removing unwanted items in the original:
foreach (var item in lstTitles.ToList())
{
if (item.StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
{
lstTitles.Remove(item);
}
}
Though this involves creating a copy of the list, which isn't really useful, as well as calling Remove() which itself is far from performant.
So you could invert your for-loop, to remove the last items first which doesn't change the indexing for unprocessed items:
for (int i = lstTitles.Count - 1; i > 0; i--)
{
if (lstTitles[i].StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
{
lstTitles.RemoveAt(i);
}
}
But as #I4V points out, all of this logic already is in List<T>.RemoveAll(), which is nicer to read and probably optimized for some edge cases, so there's little use to hand-code it again.
That's because your skipping values.
Suppose your list contains ['xVal1', 'xVal2', 'val3', 'xVal4', 'val5']. At first your i is 0, and you look at list[0], which is 'xVal1', so you remove it.
Now your list contains ['xVal2', 'val3', 'xVal4', 'val5'], and your i is 1. So you look at list[1] which is 'val3'. You ignored xVal2 !
You can start at the back of the list and go to the front, although you will still have a potential bug in case there are identical values you remove.
A shorter way would be to use LINQ:
var newList = lstTitles.Where(title=>!title.StartsWith('xxx'))
Instead of ToLower you should use the overload of StartsWith which allows to pass a StringComparison.OrdinalIgnoreCase.
Then use List.RemoveAll which is the most readable, most efficient and shortest approach:
lstTitles.RemoveAll(s => s.TrimStart().StartsWith("x", StringComparison.OrdinalIgnoreCase));
Demo
I think, you'd better just create a new list this way
list = list
.Where(i => ! i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase))
.ToList();
It would have a O(n) complexity whereas, trying to remove then 1 by 1 would be in O(n^2).
This could work also :
list.RemoveAll(i => i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase));
Handles all cases and without a second list.