Skip to content

Commit 8657b87

Browse files
committed
feat: add sort method to dictionary
1 parent 4bb924a commit 8657b87

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Microsoft.OpenApi.Extensions
6+
{
7+
/// <summary>
8+
/// Dictionary extension methods.
9+
/// </summary>
10+
public static class DictionaryExtensions
11+
{
12+
/// <summary>
13+
/// Returns a new dictionary with entries sorted by key using the default comparer.
14+
/// </summary>
15+
public static IDictionary<TKey, TValue> Sort<TKey, TValue>(
16+
this IDictionary<TKey, TValue> source)
17+
where TKey : notnull
18+
{
19+
if (source == null)
20+
throw new ArgumentNullException(nameof(source));
21+
22+
return source
23+
.OrderBy(kvp => kvp.Key)
24+
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
25+
}
26+
27+
/// <summary>
28+
/// Returns a new dictionary with entries sorted by key using a custom comparer.
29+
/// </summary>
30+
public static IDictionary<TKey, TValue> Sort<TKey, TValue>(
31+
this IDictionary<TKey, TValue> source,
32+
IComparer<TKey> comparer)
33+
where TKey : notnull
34+
{
35+
if (source == null)
36+
throw new ArgumentNullException(nameof(source));
37+
if (comparer == null)
38+
throw new ArgumentNullException(nameof(comparer));
39+
40+
return source
41+
.OrderBy(kvp => kvp.Key, comparer)
42+
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
43+
}
44+
}
45+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.OpenApi.Extensions;
4+
using Xunit;
5+
6+
namespace Microsoft.OpenApi.Tests.Extensions
7+
{
8+
public class DictionaryExtensionsTests
9+
{
10+
[Fact]
11+
public void ShouldSortStringIntDictionaryInAscendingOrder()
12+
{
13+
var dict = new Dictionary<string, int> { { "b", 2 }, { "a", 1 } };
14+
var result = dict.Sort();
15+
Assert.Equal(["a", "b"], result.Keys);
16+
}
17+
18+
[Fact]
19+
public void ShouldReturnEmptyDictionaryWhenSourceIsEmpty()
20+
{
21+
var dict = new Dictionary<string, int>();
22+
var result = dict.Sort();
23+
Assert.Empty(result);
24+
}
25+
26+
[Fact]
27+
public void ShouldKeepOrderWhenDictionaryIsAlreadySorted()
28+
{
29+
var dict = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
30+
var result = dict.Sort();
31+
Assert.Equal(["a", "b"], result.Keys);
32+
}
33+
34+
[Fact]
35+
public void ShouldSortNumericKeysNaturally()
36+
{
37+
var dict = new Dictionary<int, string> { { 10, "a" }, { 1, "b" } };
38+
var result = dict.Sort();
39+
Assert.Equal([1, 10], result.Keys);
40+
}
41+
42+
[Fact]
43+
public void ShouldSortDateTimeKeysInAscendingOrder()
44+
{
45+
var now = DateTime.Now;
46+
var later = now.AddHours(1);
47+
48+
var dict = new Dictionary<DateTime, string>
49+
{
50+
[later] = "future",
51+
[now] = "present"
52+
};
53+
54+
var result = dict.Sort();
55+
Assert.Equal([now, later], result.Keys);
56+
}
57+
58+
[Fact]
59+
public void ShouldSortWithCustomDescendingComparer()
60+
{
61+
var dict = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
62+
var result = dict.Sort(Comparer<string>.Create((x, y) => y.CompareTo(x)));
63+
Assert.Equal(["b", "a"], result.Keys);
64+
}
65+
66+
[Fact]
67+
public void ShouldSortDictionaryWithComplexValueTypes()
68+
{
69+
var dict = new Dictionary<string, ISet<string>>
70+
{
71+
{ "z", new HashSet<string> { "value1" } },
72+
{ "a", new HashSet<string> { "value2" } }
73+
};
74+
75+
var result = dict.Sort();
76+
Assert.Equal(["a", "z"], result.Keys);
77+
Assert.Equal(new HashSet<string> { "value2" }, result["a"]);
78+
}
79+
80+
[Fact]
81+
public void ShouldSortDictionaryWithNullValues()
82+
{
83+
var dict = new Dictionary<string, string>
84+
{
85+
{ "b", null },
86+
{ "a", "value" }
87+
};
88+
89+
var result = dict.Sort();
90+
Assert.Equal(["a", "b"], result.Keys);
91+
Assert.Null(result["b"]);
92+
}
93+
94+
[Fact]
95+
public void ShouldSortDictionaryOfDictionariesByOuterKey()
96+
{
97+
var dict = new Dictionary<string, Dictionary<string, string>>
98+
{
99+
["z"] = new Dictionary<string, string> { { "x", "1" } },
100+
["a"] = new Dictionary<string, string> { { "y", "2" } }
101+
};
102+
103+
var result = dict.Sort();
104+
Assert.Equal(["a", "z"], result.Keys);
105+
Assert.Equal("2", result["a"]["y"]);
106+
}
107+
}
108+
}

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ namespace Microsoft.OpenApi.Expressions
142142
}
143143
namespace Microsoft.OpenApi.Extensions
144144
{
145+
public static class DictionaryExtensions
146+
{
147+
public static System.Collections.Generic.IDictionary<TKey, TValue> Sort<TKey, TValue>(this System.Collections.Generic.IDictionary<TKey, TValue> source)
148+
where TKey : notnull { }
149+
public static System.Collections.Generic.IDictionary<TKey, TValue> Sort<TKey, TValue>(this System.Collections.Generic.IDictionary<TKey, TValue> source, System.Collections.Generic.IComparer<TKey> comparer)
150+
where TKey : notnull { }
151+
}
145152
public static class EnumExtensions
146153
{
147154
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2075", Justification="Fields are never trimmed for enum types.")]

0 commit comments

Comments
 (0)