i am using a HashSet in order to avoid having two (or more)items with the same value inside my collection , on my work i need to iterate over my hashset and remove its values but unfortunatly i cant do so , what i am trying to do is:
string newValue = "";
HashSet<string> myHashSet;
myHashSet = GetAllValues(); // lets say there is a function which fill the hashset
foreach (string s in myHashSet)
{
newValue = func(s) // lets say that func on some cases returns s as it was and
if(s != newValue) // for some cases returns another va
{
myHashSet.Remove(s);
myHashSet.Add(newValue);
}
}
thanks in advance for your kind help
You cannot modify the container while it's being iterated. The solution would be to project the initial set into a "modified" set using LINQ (Enumerable.Select), and create a new HashSet from the results of the projection.
Since if there is a func with the appropriate signature you can directly stick it into the Enumerable.Select method, and since HashSet has a constructor that accepts an IEnumerable<T>, it all comes down to one line:
var modifiedHashSet = new HashSet(myHashSet.Select(func));
The accepted answer is indeed correct, but if, as in my case, you require modification of the same instance, you can iterate through a copy of the HashSet.
foreach (string s in myHashSet.ToArray()) // ToArray will create a copy
{
newValue = func(s)
if(s != newValue)
{
myHashSet.Remove(s);
myHashSet.Add(newValue);
}
}
Related
I have a HashSet<string> with ~50k members. I have another list of objects that I'm iterating through one by one to determine if the object's email exists. If it does, I need to perform some action on the object.
var emailList = db.Emails.Select(s => s.EmailAddress.ToLower()).ToList();
var emailHash = new HashSet<string>(emailList);
var objects = db.Objects.ToList();
// everything is fine up to this point
foreach (var object in objects) {
if (!emailHash.Any(s => s.Equals(object.Email))) { // This takes ~0.3s
Console.WriteLine("Email: {0}", object.Email);
}
}
What can I do to speed up the evaluation of whether or not one string exists in a list of strings?
You are not using the HashSet correctly. Using Linq's .Any() will actually evaluate your condition against each element stored in the HashSet.
To search if an item exists in a HashSet (with constant time, O(1)) use emailHash.Contains(object.Email).
One obvious change is to not use the Enumerable.Any() LINQ function, which basically negates the advantages of using a hash set by performing a sequential search.
Instead, use HashSet's built-in Contains(string) function:
foreach (var object in objects) {
if (!emailHash.Contains(object.Email)) {
Console.WriteLine("Email: {0}", object.Email);
}
}
// Get list from DB
List<Category> dbCategories = DatabaseWrapper.GetCategories();
...
// COPY values to Obs.Col.
var shownCategries = new ObservableCollection<Category>(dbCategories);
// This also changes the value in 'dbCatagories'.
shownCategories[0].Name = "Hej";
I want to be able to change a value in the obs.col. without changing the same value in dbCategories. I can't figure out what I'm doing wrong since the 2nd line of code should be a copy constructor?
Nothing can explain more than source code. You're making invalid assumptions.
Namely:
private void CopyFrom(IEnumerable<T> collection)
{
IList<T> items = this.Items;
if (collection == null || items == null)
return;
foreach (T obj in collection)
items.Add(obj);
}
and
public ObservableCollection(List<T> list)
: base(list != null ? (IList<T>) new List<T>(list.Count) : (IList<T>) list)
{
this.CopyFrom((IEnumerable<T>) list);
}
as you can see, "copy constructor" indeed does make copy of LIST object itself, but it does not make copy of individual list elements. Note, it's not that simple, your example could actually work if Category would be struct.
This is usual behaviour, and it's called shallow copying. Alternatively, what you are looking is called deep copying
You need to create manually copies, either using serialization, or just create copy-constructor in Category class.
This would be good example:
new ObservableCollection(dbCategories.Select(x => new Category(x)).ToList());
I got a List<string> named Test:
List<string> Test = new List<string>();
I want to add a string to it using Test.Add();, but first I want to check if it already exists in the list.
I thought of something like this:
if (Test.Find("Teststring") != true)
{
Test.Add("Teststring");
}
However, this returns an error.
I assume that you if you don't want to add the item if it is already added
Try This:
if (!Test.Contains("Teststring"))
{
Test.Add("Teststring");
}
Any receives a Predicate. It determines if any element in a collection matches a certain condition. You could do this imperatively, using a loop construct. But the Any extension method provides another way.
See this:
bool b1 = Test.Any(item => item == "Teststring");
Also you can use :
if (!Test.Contains("Teststring"))
{
...
}
If you don't want to add an item twice it is a good indicator that you might use a HashSet<T> instead which is more efficient but doesn't allow duplicates(like a Dictionary with only keys).
HashSet<string> Test = new HashSet<string>();
bool newString = Test.Add("Teststring");
If you need to use the list use List.Contains to check if the string is already in the list.
What is the difference between HashSet and List in C#?
But your code suggests that you only want to add duplicates. I assume that this is not intended.
In my opinion you are using the wrong datastructure here. You should use Hashset to avoid duplicates.
The lookup time for Hashset is O(1) whereas for list it is O(n)
The HashSet class provides high-performance set operations. A set is a collection that contains no duplicate elements, and whose elements are in no particular order.
This is how your code should look like.
HashSet<string> Test = new HashSet<string>();
Test.Add("Teststring");
Use Test.Contains("TestString");
I have a dictionary of type Dictionary<string, IEnumerable<string>> and a list of string values. For some reason, every time I do an Add, every value in the dictionary is overwritten. I'm completely stumped as to why this is happening. I made sure it's not a reference problem be declaring and initializing the IEnumberable object within the loop so that it's scope does not go outside one iteration, and it still does it. Here is my code:
foreach (string type in typelist)
{
IEnumerable<string> lst =
from row in root.Descendants()
where row.Attribute("serial").Value.Substring(0, 3).Equals(type)
select row.Attribute("serial").Value.Substring(3).ToLower();
serialLists.Add(type, lst);
}
where typelist is an IEnumerable<string>, root is an XElement, and serialLists is my Dictionary.
This is a captured iterator problem.
Try:
foreach (string tmp in typelist)
{
string type = tmp;
(and the rest unchanged)
Alternatively, I would evaluate the expression during the add, I.e. do a .ToList() in the .Add:
serialLists.Add(type, lst.ToList());
The second option is probably more effective overall, although it does force evaluation of thigs that might otherwise never be needed.
The reason is that your IEnumerable<string> sequences are not being populated eagerly, but on-demand, after the foreach loop would have completed all its iterations. Thus, when any IEnumerable<string> sequence is enumerated, the type variable would always have the value of the last element in typelist.
Here is one easy way to fix it:
foreach (string type in typelist)
{
string typeCaptured = type;
IEnumerable<string> lst =
from row in root.Descendants()
where row.Attribute("serial").Value.Substring(0, 3).Equals(typeCaptured)
select row.Attribute("serial").Value.Substring(3).ToLower();
serialLists.Add(typeCaptured, lst);
}
I have to write a query in a web application using LINQ but I need to change that query into an array list. How can I change the query below to do this?
var resultsQuery =
from result in o["SearchResponse"]["Web"]["Results"].Children()
select new
{
Url = result.Value<string>("Url").ToString(),
Title = result.Value<string>("Title").ToString(),
Content = result.Value<string>("Description").ToString()
};
If you really need to create an ArrayList, you can write new ArrayList(resultsQuery.ToArray()).
However, you should use a List<T> instead, by writing resultsQuery.ToList().
Note that, in both cases, the list will contain objects of anonymous type.
There is a .ToArray() method that'll convert IEnumerable to an Array.
ArrayList doesn't have a constructor or Add(Range) method that takes an IEnumerable. So that leaves two choices:
Use an intermediate collection that does implement ICollection: as both Array and List<T> implement ICollection can be used via the ToArray() or ToList() extension methods from LINQ.
Create an instance of ArrayList and then add each element of the result:
var query = /* LINQ Expression */
var res = new ArrayList();
foreach (var item in query) {
res.Add(item);
}
The former method is simple to do but does mean creating the intermediate data structure (which of the two options has a higher overhead is an interesting question and partly depends on the query so there is no general answer). The latter is more code and does involve growing the ArrayList incrementally (so more memory for the GC, as would be the case for an intermediate Array or List<T>).
If you just need this in one place you can just do the code inline, if you need to do it in multiple places create your own extension method over IEnumerable<T>:
public static class MyExtensions {
public static ArrayList ToArrayList<T>(this IEnumerable<T> input) {
var col = input as ICollection;
if (col != null) {
return new ArrayList(col);
}
var res = new ArrayList();
foreach (var item in input) {
res.Add(item);
}
return res;
}
}