-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[API Proposal]: Add GetViewBetween method to SortedDictionary #77645
Comments
Tagging subscribers to this area: @dotnet/area-system-collections Issue DetailsBackground and motivationThis is an intentional duplicate of #26754, which was closed by the author without a resolution.
The conversion from private SortedDictionary(TreeSet<KeyValuePair<TKey, TValue>> set)
{
_set = set;
} API Proposalnamespace System.Collections.Generic;
public class SortedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue> where TKey : notnull
{
public SortedDictionary<TKey, TValue> GetViewBetween(TKey lowerValue, TKey upperValue);
} Example implementation: public SortedDictionary<TKey, TValue> GetViewBetween(TKey lowerValue, TKey upperValue)
{
var setView = _set.GetViewBetween(new(lowerValue, default!), new(upperValue, default!));
// Note: this conversion (`TreeSubSet` to `TreeSet`) is potentially problematic. There should be no need for a deep copy here,
// but the existing public constructors don't make it easy to avoid.
var treeSet = new TreeSet<KeyValuePair<TKey, TValue>>(setView, _set.Comparer);
return new SortedDictionary<TKey, TValue>(treeSet);
} API Usagevar dic = new SortedDictionary<int, string>()
{
{ 1, "a" },
{ 2, "b" },
{ 3, "c" },
};
var view = dic.GetViewBetween(1, 2);
foreach (var (key, value) in view)
{
// Do something
}
var largestValueInRange = view.Values.Max();
// Note: Reverse would currently use the inefficient Linq version, but this could be optimised in future to enumerate the tree backwards.
var backwardKeys = view.Keys.Reverse().ToList(); Alternative Designs
public IEnumerable<KeyValuePair<TKey, TValue>> GetViewBetween(TKey lowerValue, TKey upperValue); The main advantages of this approach would be:
However, this has a number of disadvantages:
public SortedSet<KeyValuePair<Tkey, TValue>> GetViewBetween(TKey lowerValue, TKey upperValue); This has a few issues, as mentioned in #26754 (comment), and doesn't seem worthwhile to consider.
RisksThis would not require any breaking changes that I'm aware of, aside from adding a couple of private/internal constructors to make the change possible. One potential consideration here is binary serialization, but I'm not aware of any specific breaking changes here. As mentioned above, it may be difficult to add a non-copying conversion from Exposing a mutable view of the dictionary would require some work to verify that it is not possible to corrupt the dictionary via the view, or vice versa. This also adds some design questions. Quoting from @terrajobst in #26754 (comment):
|
Can someone who knows more than me about binary serialization let me know whether it would be possible to do the following to get around the
Concerns here are:
|
Another related question is whether |
In .NET 8 it may be possible to simply break binary formatting for this type in some cases: https://github.com/dotnet/designs/blob/main/accepted/2020/better-obsoletion/binaryformatter-obsoletion.md |
@Jcparkyn I love this proposal! What do you think about expanding it? The underlying
These help solve 2 of the examples you gave: // use the following line instead of: var largestValueInRange = view.Values.Max();
var largestValueInRange = view.Max?.Value;
// use the following line instead of var backwardKeys = view.Keys.Reverse().ToList();
var backwardKeys = view.Reverse().Select(x => x.Keys).ToList();
|
You could even add these members to |
@smarts +1 for adding Min/Max to KeyCollection, created a proposal for that but got redirected here. |
I definitely agree with others that Min/Max should be added, regardless of whether this proposal ever makes it through (but both would be nice). |
Updated API shape based on other comments: namespace System.Collections.Generic;
public class SortedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue> where TKey : notnull
{
+ public KeyValuePair<TKey, TValue>? Min { get; }
+ public KeyValuePair<TKey, TValue>? Max { get; }
+ public IEnumerable<KeyValuePair<TKey, TValue>> Reverse()
+ public SortedDictionary<TKey, TValue> GetViewBetween(TKey? lowerValue, TValue? upperValue)
}
|
Background and motivation
This is an intentional duplicate of #26754, which was closed by the author without a resolution.
SortedDictionary
doesn't have a efficient way to enumerate through a subset of the elements. All the ways to do this currently require iterating over all elements in the dictionary, which is inefficient if the desired range is much smaller than the whole collection. For users that need this option, the only current solution is to just use aSortedSet<KeyValuePair<TKey, TValue>>
directly, instead of usingSortedDictionary
.SortedDictionary
uses aSortedSet<T>
internally, which exposes aGetViewBetween
method for this purpose. This proposal would add aGetViewBetween
method toSortedDictionary
, which would internally use_set.GetViewBetween
.The conversion from
SortedSet
toSortedDictionary
can be done efficiently, provided that we create a new private constructor like so:API Proposal
Example implementation:
API Usage
Some practical use-cases for this could be e.g.:
x
(dic.GetViewBetween(x, int.MaxValue).First()
)dic.GetViewBetween("a", "b")
)dic.GetViewBetween(1000, 2000).Values.Sum()
)None of these use-cases can currently be achieved without iterating over all of the items (at least until the upper bound), and as far as I'm aware, none of them can be achieved with other standard collection types (except
SortedSet<KeyValuePair<Tkey, TValue>>
).Alternative Designs
SortedDictionary
, we could just expose anIEnumberable
like so:The main advantages of this approach would be:
SortedSet
toTreeSet
.However, this has a number of disadvantages:
Keys
andValues
properties that already exist onSortedDictionary
.SortedDictionary
, but in theory that could be added separately in the future, copying the existing implementation forSortedSet.Reverse
.SortedSet
it would seem reasonable to keep them here as well.IEnumberable
would require boxing (I think?). However, the overhead of wrapping theSortedSet.TreeSubSet
in aSortedDictionary
might negate this.SortedDictionary
, we could use the return theSortedSet
directly like so (as per the original proposal from SortedDictionary should have GetViewBetween method #26754:This has a few issues, as mentioned in #26754 (comment), and doesn't seem worthwhile to consider.
SortedSet.GetViewBetween
does not provide this directly, even though there is a constructor forTreeSubSet
that supports it. Edit: See Add unbounded SortedSet<T>.TreeSubSet API #55510.Risks
This would not require any breaking changes that I'm aware of, aside from adding a couple of private/internal constructors to make the change possible. One potential consideration here is binary serialization, but I'm not aware of any specific breaking changes here.
As mentioned above, it may be difficult to add a non-copying conversion from
TreeSubSet
toTreeSet
.SortedDictionary
needs to store aTreeSet
internally for backwards-compatibility with binary serialization.Exposing a mutable view of the dictionary would require some work to verify that it is not possible to corrupt the dictionary via the view, or vice versa. This also adds some design questions. Quoting from @terrajobst in #26754 (comment):
The text was updated successfully, but these errors were encountered: