Reputation: 21072
UPDATE:
Starting with .Net 4.7.2, HashSet.TryGetValue - docs is available.
HashSet.TryGetValue - SO post
I have a problem with HashSet
because it does not provide any method similar to TryGetValue
known from Dictionary
. And I need such method -- passing element to find in the set, and set returning element from its collection (when found).
Sidenote -- "why do you need element from the set, you already have that element?". No, I don't, equality and identity are two different things.
HashSet
is not sealed but all its fields are private, so deriving from it is pointless. I cannot use Dictionary
instead because I need SetEquals
method. I was thinking about grabbing a source for HashSet
and adding desired method, but the license is not truly open source (I can look, but I cannot distribute/modify). I could use reflection but the arrays in HashSet
are not readonly
meaning I cannot bind to those fields once per instance lifetime.
And I don't want to use full blown library for just single class.
So far I am stuck with LINQ SingleOrDefault
. So the question is how fix this -- have HashSet
with TryGetValue
?
Upvotes: 4
Views: 3927
Reputation: 21243
Finally added in .NET 4.7.2:
HashSet.TryGetValue(T, T) Method
Upvotes: 2
Reputation:
hopefully not blind but I haven't seen this answer anywhere. If you want dictionary's TryGetValue
, you can just steal it.
theHashset.ToDictionary(item => item.ID).TryGetValue(key, out value)
All you need is a quick lambda for determining unique keys.
Upvotes: 1
Reputation: 1500875
I agree this is something which is basically missing. While it's only useful in rare cases, I think they're significant rare cases - most notable, key canonicalization.
I can only think of one suggestion at the moment, and it's truly foul.
You can specify your own IEqualityComparer<T>
when creating a HashSet<T>
- so create one which remembers the arguments to the last positive (i.e. true-returning) Equals
comparison it has performed. You can then call Contains
, and see what the equality comparer was asked to compare.
Caveats:
HashSet<T>
doesn't use any optimization such as "if the references are equal, don't bother consulting the equality comparer"I've been trying to think of other alternatives in terms of finding intersections, but I haven't got anywhere yet...
As noted in comments, it would be worth encapsulating this as far as possible - I suspect you only need a very limited set of operations, so I'd wrap a HashSet<T>
in your own class and only expose the operations you really need - that way you get to clear the "cache" after each operation, removing my first objection above.
It still feels like a horrible abuse to me, but...
As others have suggested, an alternative would be to use a Dictionary<TKey, TValue>
and implement SetEquals
yourself. That would be simple enough to do - and again, you'd want to encapsulate this in your own type. Either way, you should probably design the type itself first, and then implement it using either a HashSet<>
or a Dictionary<,>
as an implementation detail.
Upvotes: 3
Reputation: 9772
Probably you should switch from a HashSet
to a SortedSet
There is a simple TryGetValue()
for a SortedSet
:
public bool TryGetValue(ref T element)
{
var foundSet = sortedSet.GetViewBetween(element, element);
if(foundSet.Count == 1)
{
element = foundSet.First();
return true;
}
return false;
}
when called, the element needs just all properties set which are used in the Comparer. It returns the element found in the Set.
Upvotes: 4
Reputation: 789
Sounds like you trying to use the wrong tool. True, you can save some memory using a HashSet but it seems to me that you are trying to acheeve a different goal: Get the actual element that is just equal to a representation. So in reality they are two different elements. Just the memento (a unique representation) is equal.
Therefore you'd be better of using a Dictionary where you add your elements as Key and Value. So you're able to get it back (the identical) but you miss your SetEquals
....
I suppose SetEquals
in it's implementation does nothing much different than sequencially compare two HashSets in it's bucket order and fails on first non-equality.
So you should be equally good off using a simple SequenceEqual()
(LINQ) comparing the two Keys
collections.
So this extension method could do
public static SetEqual<T,G>(this IDictionary<T,G> d, IDictionary<T,G> e)
{
return d.Keys.SequenceEqual(e.Keys);
}
This should work, because a Dictionary
basically is a HashSet with an associated value. And more appropriate to your problem. (OK, to be correct, the code should go for Dictionary<>
instead of IDictionary<>
because Key order matters)
If you need an IEnumerable<>
on the second parameter try sorting to get a defined order (not so efficient).
Upvotes: 3