Is there a quicker or more efficient way to add Strings to a List than the below example?:
List<String> apptList = new List<String>();
foreach (Appointment appointment in appointments){
String subject = appointment.Subject;
//...(continues for another 10 lines)
//...And then manually adding each String to the List:
apptList.Add(subject);
//...(continues for another 10 lines)
//And then send off List apptList to another method
}
var apptList = appointments.Select(a => a.Subject).ToList();
I'm not sure if I'm getting your code right, but since your Appointment class is already implementing IEnumerable, you should be able to call ToList() to convert it to a list in one shot.
http://msdn.microsoft.com/en-us/library/bb342261.aspx
How about this:
List<string> apptList = appointments.Select(x => x.Subject).ToList();
Related
I have following string in my aspx.cs page. These both strings are dynamic
string fromDB = "12,24,25,45,67,78,90" (its a dynamic string)
I have another string which has two or more values always as given below
string anotherStr = "24,67"
I am not getting an idea how to remove "anotherStr" values from "fromDB"
Finally I need "12,25,45,78,90". I am not getting an idea how to do this using c#.
using Linq:
string.Join(",", fromDB.Split(',').Except(anotherStr.Split(',')))
Split your (hard) problem into multiple (easy) problems:
Convert your comma-separated strings into lists (or arrays) of strings (using e.g. String.Split).
Find out how to remove all list entries from one list from a second list (using e.g. Enumerable.Except).
Convert your list back into a comma-separated string (using e.g. String.Join).
For all of these simpler problems, solutions can be found here on SO.
string fromDB = "12,24,25,45,67,78,90";
string anotherStr = "24,67";
var result = fromDB.Split(',').Except(anotherStr.Split(',')).ToList();
Console.WriteLine(string.Join(",", result));
Console.ReadLine();
This splits the strings into arrays, and then excludes any entries from fromDB that are also in anotherStr.
Note that the use of Except means that any duplicates will be removed from fromDB - as per https://stackoverflow.com/a/44547153/34092 .
You could do something like
HashSet<string> all = new HashSet<string>(fromDB.Split(',')),
toRemove = new HashSet<string>(anotherStr.Split(','));
foreach(var item in toRemove) {
all.Remove(item);
}
I suggest using HashSet<T> and Linq:
HashSet<string> exclude = new HashSet<string>(anotherStr.Split(','));
string result = string.Join(",", fromDB
.Split(',')
.Where(item => !exclude.Contains(item)));
Please, notice, that Except removes duplicates within fromDB while Where preserves them.
You split both strings to get two arrays, making the first a List<string> :
var fromDbList = fromDb.Split(',').ToList();
var anotherStrArray = anotherStr.Split(',');
You loop the second array, and delete its values from the first (which you cannot do on a String[], hence the previous ToList())
foreach (var valueToDelete in anotherStrArray)
{
fromDbList.Remove(valueToDelete);
}
Then you join the (now modified) first array to get a single string:
var finalString = String.Join(fromDbList, ",");
I have a list as follows,
List<string> variablelist = new List<string> { "first", "second","third"};
And i have one more list, like
List<string> method = new List<string>();
//somecode
method.Add(workingline);
I want to check if any of the element of variablelist is NOT present in methodlist and also I want get that Particular ELEMENT.
Thanks in Advance
LINQ is the simplest way of doing this, with the Except method:
var inOnlyVariableList = variableList.Except(method).ToList();
The result will be a List<string> of strings which are in variableList but not in method.
this should be another solution to it.
var list = variablelist.Where(ItemName => !method.Contains(ItemName)).ToList();
I want to compare two lists and get the valid words into a new list.
var words = new List<string>();
var badWords = new List<string>();
//this is just an example list. actual list does contain 700 records
words.Add("Apple");
words.Add("Moron");
words.Add("Seafood");
words.Add("Cars");
words.Add("Chicken");
words.Add("Twat");
words.Add("Watch");
words.Add("Android");
words.Add("c-sharp");
words.Add("Fool");
badWords.Add("Idiot");
badWords.Add("Retarded");
badWords.Add("Twat");
badWords.Add("Fool");
badWords.Add("Moron");
I am looking for most efficient way to compare the lists and put all the 'good' words into a new list. The finalList shouldn't contain "Moron", "Twat" and "Fool".
var finalList = new List<string>();
Or is it unnecessary to create a new List? I am happy to hear your ideas!
Thank you in advance
Use EnumerableExcept function storing in System.Linq namespace
finalList = words.Except(badWords).ToList();
Most efficient way to save your time and also the fastest way to do it, because Except implementation uses Set, which is fast
Use Enumerable.Except:
List<string> cleanList = words.Except(badWords).ToList();
This is efficient because Except uses a set based approach.
An even more efficient approach is to avoid that "bad" words are added to the first list at all. For example by using a HashSet<string> with a case-insensitive comparer:
var badWords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase){ "Idiot", "Retarded", "Twat", "Fool", "Moron" };
string word = "idiot";
if (!badWords.Contains(word))
words.Add(word);
https://msdn.microsoft.com/library/bb908822(v=vs.90).aspx
var words = new List<string>();
var badWords = new List<string>();
//this is just an example list. actual list does contain 700 records
words.Add("Apple");
words.Add("Moron");
words.Add("Seafood");
words.Add("Cars");
words.Add("Chicken");
words.Add("Twat");
words.Add("Watch");
words.Add("Android");
words.Add("c-sharp");
words.Add("Fool");
badWords.Add("Idiot");
badWords.Add("Retarded");
badWords.Add("Twat");
badWords.Add("Fool");
badWords.Add("Moron");
var result = words.Except(badWords).ToList();
Edit: Got in late.
you can use contains method
words.Where(g=>!badWords.Contains(g)).ToList()
If your don't want to create a new List you can remove the bad words from your existing List with RemoveAll()
words.RemoveAll(badWords.Contains);
If I have:
List<string> myList1;
List<string> myList2;
myList1 = getMeAList();
// Checked myList1, it contains 4 strings
myList2 = getMeAnotherList();
// Checked myList2, it contains 6 strings
myList1.Concat(myList2);
// Checked mylist1, it contains 4 strings... why?
I ran code similar to this in Visual Studio 2008 and set break points after each execution. After myList1 = getMeAList();, myList1 contains four strings, and I pressed the plus button to make sure they weren't all nulls.
After myList2 = getMeAnotherList();, myList2 contains six strings, and I checked to make sure they weren't null... After myList1.Concat(myList2); myList1 contained only four strings. Why is that?
Concat returns a new sequence without modifying the original list. Try myList1.AddRange(myList2).
Try this:
myList1 = myList1.Concat(myList2).ToList();
Concat returns an IEnumerable<T> that is the two lists put together, it doesn't modify either existing list. Also, since it returns an IEnumerable, if you want to assign it to a variable that is List<T>, you'll have to call ToList() on the IEnumerable<T> that is returned.
targetList = list1.Concat(list2).ToList();
It's working fine I think so. As previously said, Concat returns a new sequence and while converting the result to List, it does the job perfectly.
It also worth noting that Concat works in constant time and in constant memory.
For example, the following code
long boundary = 60000000;
for (long i = 0; i < boundary; i++)
{
list1.Add(i);
list2.Add(i);
}
var listConcat = list1.Concat(list2);
var list = listConcat.ToList();
list1.AddRange(list2);
gives the following timing/memory metrics:
After lists filled mem used: 1048730 KB
concat two enumerables: 00:00:00.0023309 mem used: 1048730 KB
convert concat to list: 00:00:03.7430633 mem used: 2097307 KB
list1.AddRange(list2) : 00:00:00.8439870 mem used: 2621595 KB
I know this is old but I came upon this post quickly thinking Concat would be my answer. Union worked great for me. Note, it returns only unique values but knowing that I was getting unique values anyway this solution worked for me.
namespace TestProject
{
public partial class Form1 :Form
{
public Form1()
{
InitializeComponent();
List<string> FirstList = new List<string>();
FirstList.Add("1234");
FirstList.Add("4567");
// In my code, I know I would not have this here but I put it in as a demonstration that it will not be in the secondList twice
FirstList.Add("Three");
List<string> secondList = GetList(FirstList);
foreach (string item in secondList)
Console.WriteLine(item);
}
private List<String> GetList(List<string> SortBy)
{
List<string> list = new List<string>();
list.Add("One");
list.Add("Two");
list.Add("Three");
list = list.Union(SortBy).ToList();
return list;
}
}
}
The output is:
One
Two
Three
1234
4567
Take a look at my implementation. It's safe from null lists.
IList<string> all= new List<string>();
if (letterForm.SecretaryPhone!=null)// first list may be null
all=all.Concat(letterForm.SecretaryPhone).ToList();
if (letterForm.EmployeePhone != null)// second list may be null
all= all.Concat(letterForm.EmployeePhone).ToList();
if (letterForm.DepartmentManagerName != null) // this is not list (its just string variable) so wrap it inside list then concat it
all = all.Concat(new []{letterForm.DepartmentManagerPhone}).ToList();
The example below throws an InvalidOperationException, "Collection was modified; enumeration operation may not execute." when executing the code.
var urls = new List<string>();
urls.Add("http://www.google.com");
foreach (string url in urls)
{
// Get all links from the url
List<string> newUrls = GetLinks(url);
urls.AddRange(newUrls); // <-- This is really the problematic row, adding values to the collection I'm looping
}
How can I rewrite this in a better way? I'm guessing a recursive solution?
You can't, basically. What you really want here is a queue:
var urls = new Queue<string>();
urls.Enqueue("http://www.google.com");
while(urls.Count != 0)
{
String url = url.Dequeue();
// Get all links from the url
List<string> newUrls = GetLinks(url);
foreach (string newUrl in newUrls)
{
queue.Enqueue(newUrl);
}
}
It's slightly ugly due to there not being an AddRange method in Queue<T> but I think it's basically what you want.
There are three strategies you can use.
Copy the List<> to a second collection (list or array - perhaps use ToArray()). Loop through that second collection, adding urls to the first.
Create a second List<>, and loop through your urls List<> adding new values to the second list. Copy those to the original list when done looping.
Use a for loop instead of a foreach loop. Grab your count up front. List should leave things indexed correctly, so it you add things they will go to the end of the list.
I prefer #3 as it doesn't have any of the overhead associated with #1 or #2. Here is an example:
var urls = new List<string>();
urls.Add("http://www.google.com");
int count = urls.Count;
for (int index = 0; index < count; index++)
{
// Get all links from the url
List<string> newUrls = GetLinks(urls[index]);
urls.AddRange(newUrls);
}
Edit: The last example (#3) assumes that you don't want to process additional URLs as they are found in the loop. If you do want to process additional URLs as they are found, just use urls.Count in the for loop instead of the local count variable as mentioned by configurator in the comments for this answer.
Use foreach with a lambda, it's more fun!
var urls = new List<string>();
var destUrls = new List<string>();
urls.Add("http://www.google.com");
urls.ForEach(i => destUrls.Add(GetLinks(i)));
urls.AddRange(destUrls);
alternately, you could treat the collection as a queue
IList<string> urls = new List<string>();
urls.Add("http://www.google.com");
while (urls.Count > 0)
{
string url = urls[0];
urls.RemoveAt(0);
// Get all links from the url
List<string> newUrls = GetLinks(url);
urls.AddRange(newUrls);
}
I would create two lists add into the second and then update the reference like this:
var urls = new List<string>();
var destUrls = new List<string>(urls);
urls.Add("http://www.google.com");
foreach (string url in urls)
{
// Get all links from the url
List<string> newUrls = GetLinks(url);
destUrls.AddRange(newUrls);
}
urls = destUrls;
Consider using a Queue with while loop (while q.Count > 0, url = q.Dequeue()) instead of iteration.
I assume you want to iterate over the whole list, and each item you add to it? If so I would suggest recursion:
var urls = new List<string>();
var turls = new List<string();
turls.Add("http://www.google.com")
iterate(turls);
function iterate(List<string> u)
{
foreach(string url in u)
{
List<string> newUrls = GetLinks(url);
urls.AddRange(newUrls);
iterate(newUrls);
}
}
You can probably also create a recursive function, like this (untested):
IEnumerable<string> GetUrl(string url)
{
foreach(string u in GetUrl(url))
yield return u;
foreach(string ret_url in WHERE_I_GET_MY_URLS)
yield return ret_url;
}
List<string> MyEnumerateFunction()
{
return new List<string>(GetUrl("http://www.google.com"));
}
In this case, you will not have to create two lists, since GetUrl does all the work.
But I may have missed the point of you program.
Don't change the collection you're looping through via for each. Just use a while loop on the Count property of the list and access the List items by index. This way, even if you add items, the iteration should pick up the changes.
Edit: Then again, it sort of depends on whether you WANT the new items you added to be picked up by the loop. If not, then this won't help.
Edit 2: I guess the easiest way to do it would be to just change your loop to:
foreach (string url in urls.ToArray())
This will create an Array copy of your list, and it will loop through this instead of the original list. This will have the effect of not looping over your added items.
Jon's approach is right; a queue's the right data structure for this kind of application.
Assuming that you'd eventually like your program to terminate, I'd suggest two other things:
don't use string for your URLs, use System.Web.Uri: it provides a canonical string representation of the URL. This will be useful for the second suggestion, which is...
put the canonical string representation of each URL you process in a Dictionary. Before you enqueue a URL, check to see if it's in the Dictionary first.
It's hard to make the code better without knowing what GetLinks() does. In any event, this avoids recursion. The standard idiom is you don't alter a collection when you're enumerating over it. While the runtime could have let you do it, the reasoning is that it's a source of error, so better to create a new collection or control the iteration yourself.
create a queue with all urls.
when dequeueing, we're pretty much saying we've processed it, so add it to result.
If GetLinks() returns anything, add those to the queue and process them as well.
.
public List<string> ExpandLinksOrSomething(List<string> urls)
{
List<string> result = new List<string>();
Queue<string> queue = new Queue<string>(urls);
while (queue.Any())
{
string url = queue.Dequeue();
result.Add(url);
foreach( string newResult in GetLinks(url) )
{
queue.Enqueue(newResult);
}
}
return result;
}
The naive implementation assumes that GetLinks() will not return circular references. e.g. A returns B, and B returns A. This can be fixed by:
List<string> newItems = GetLinks(url).Except(result).ToList();
foreach( string newResult in newItems )
{
queue.Enqueue(newResult);
}
* As others point out using a dictionary may be more efficient depending on how many items you process.
I find it strange that GetLinks() would return a value, and then later resolve that to more Url's. Maybe all you want to do is 1-level expansion. If so, we can get rid of the Queue altogether.
public static List<string> StraightProcess(List<string> urls)
{
List<string> result = new List<string>();
foreach (string url in urls)
{
result.Add(url);
result.AddRange(GetLinks(url));
}
return result;
}
I decided to rewrite it because while other answers used queues, it wasn't apparent that they didn't run forever.