From 8657b87030758611ef577b3678925a905b46761a Mon Sep 17 00:00:00 2001 From: Michael Mutunga Date: Tue, 8 Apr 2025 15:27:44 +0300 Subject: [PATCH 1/3] feat: add sort method to dictionary --- .../Extensions/DictionaryExtensions.cs | 45 ++++++++ .../Extensions/DictionaryExtensionsTests.cs | 108 ++++++++++++++++++ .../PublicApi/PublicApi.approved.txt | 7 ++ 3 files changed, 160 insertions(+) create mode 100644 src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs create mode 100644 test/Microsoft.OpenApi.Tests/Extensions/DictionaryExtensionsTests.cs diff --git a/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs new file mode 100644 index 000000000..60ec859a3 --- /dev/null +++ b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.OpenApi.Extensions +{ + /// + /// Dictionary extension methods. + /// + public static class DictionaryExtensions + { + /// + /// Returns a new dictionary with entries sorted by key using the default comparer. + /// + public static IDictionary Sort( + this IDictionary source) + where TKey : notnull + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return source + .OrderBy(kvp => kvp.Key) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + + /// + /// Returns a new dictionary with entries sorted by key using a custom comparer. + /// + public static IDictionary Sort( + this IDictionary source, + IComparer comparer) + where TKey : notnull + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (comparer == null) + throw new ArgumentNullException(nameof(comparer)); + + return source + .OrderBy(kvp => kvp.Key, comparer) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Extensions/DictionaryExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Extensions/DictionaryExtensionsTests.cs new file mode 100644 index 000000000..e00dba156 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Extensions/DictionaryExtensionsTests.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using Microsoft.OpenApi.Extensions; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Extensions +{ + public class DictionaryExtensionsTests + { + [Fact] + public void ShouldSortStringIntDictionaryInAscendingOrder() + { + var dict = new Dictionary { { "b", 2 }, { "a", 1 } }; + var result = dict.Sort(); + Assert.Equal(["a", "b"], result.Keys); + } + + [Fact] + public void ShouldReturnEmptyDictionaryWhenSourceIsEmpty() + { + var dict = new Dictionary(); + var result = dict.Sort(); + Assert.Empty(result); + } + + [Fact] + public void ShouldKeepOrderWhenDictionaryIsAlreadySorted() + { + var dict = new Dictionary { { "a", 1 }, { "b", 2 } }; + var result = dict.Sort(); + Assert.Equal(["a", "b"], result.Keys); + } + + [Fact] + public void ShouldSortNumericKeysNaturally() + { + var dict = new Dictionary { { 10, "a" }, { 1, "b" } }; + var result = dict.Sort(); + Assert.Equal([1, 10], result.Keys); + } + + [Fact] + public void ShouldSortDateTimeKeysInAscendingOrder() + { + var now = DateTime.Now; + var later = now.AddHours(1); + + var dict = new Dictionary + { + [later] = "future", + [now] = "present" + }; + + var result = dict.Sort(); + Assert.Equal([now, later], result.Keys); + } + + [Fact] + public void ShouldSortWithCustomDescendingComparer() + { + var dict = new Dictionary { { "a", 1 }, { "b", 2 } }; + var result = dict.Sort(Comparer.Create((x, y) => y.CompareTo(x))); + Assert.Equal(["b", "a"], result.Keys); + } + + [Fact] + public void ShouldSortDictionaryWithComplexValueTypes() + { + var dict = new Dictionary> + { + { "z", new HashSet { "value1" } }, + { "a", new HashSet { "value2" } } + }; + + var result = dict.Sort(); + Assert.Equal(["a", "z"], result.Keys); + Assert.Equal(new HashSet { "value2" }, result["a"]); + } + + [Fact] + public void ShouldSortDictionaryWithNullValues() + { + var dict = new Dictionary + { + { "b", null }, + { "a", "value" } + }; + + var result = dict.Sort(); + Assert.Equal(["a", "b"], result.Keys); + Assert.Null(result["b"]); + } + + [Fact] + public void ShouldSortDictionaryOfDictionariesByOuterKey() + { + var dict = new Dictionary> + { + ["z"] = new Dictionary { { "x", "1" } }, + ["a"] = new Dictionary { { "y", "2" } } + }; + + var result = dict.Sort(); + Assert.Equal(["a", "z"], result.Keys); + Assert.Equal("2", result["a"]["y"]); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 755a9e17e..26b553288 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -142,6 +142,13 @@ namespace Microsoft.OpenApi.Expressions } namespace Microsoft.OpenApi.Extensions { + public static class DictionaryExtensions + { + public static System.Collections.Generic.IDictionary Sort(this System.Collections.Generic.IDictionary source) + where TKey : notnull { } + public static System.Collections.Generic.IDictionary Sort(this System.Collections.Generic.IDictionary source, System.Collections.Generic.IComparer comparer) + where TKey : notnull { } + } public static class EnumExtensions { [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2075", Justification="Fields are never trimmed for enum types.")] From 37e130d45f196d1048dcdb435decc531611a5ab3 Mon Sep 17 00:00:00 2001 From: Michael Mutunga Date: Tue, 8 Apr 2025 16:50:53 +0300 Subject: [PATCH 2/3] update dictionary extension --- .../Extensions/DictionaryExtensions.cs | 21 +++++++------------ .../PublicApi/PublicApi.approved.txt | 4 ++-- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs index 60ec859a3..41741f644 100644 --- a/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs +++ b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs @@ -12,34 +12,27 @@ public static class DictionaryExtensions /// /// Returns a new dictionary with entries sorted by key using the default comparer. /// - public static IDictionary Sort( + public static SortedDictionary Sort( this IDictionary source) where TKey : notnull { - if (source == null) - throw new ArgumentNullException(nameof(source)); + Utils.CheckArgumentNull(source); - return source - .OrderBy(kvp => kvp.Key) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + return new SortedDictionary(source); } /// /// Returns a new dictionary with entries sorted by key using a custom comparer. /// - public static IDictionary Sort( + public static SortedDictionary Sort( this IDictionary source, IComparer comparer) where TKey : notnull { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (comparer == null) - throw new ArgumentNullException(nameof(comparer)); + Utils.CheckArgumentNull(source); + Utils.CheckArgumentNull(comparer); - return source - .OrderBy(kvp => kvp.Key, comparer) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + return new SortedDictionary(source, comparer); } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 26b553288..e619fc6c3 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -144,9 +144,9 @@ namespace Microsoft.OpenApi.Extensions { public static class DictionaryExtensions { - public static System.Collections.Generic.IDictionary Sort(this System.Collections.Generic.IDictionary source) + public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.IDictionary source) where TKey : notnull { } - public static System.Collections.Generic.IDictionary Sort(this System.Collections.Generic.IDictionary source, System.Collections.Generic.IComparer comparer) + public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.IDictionary source, System.Collections.Generic.IComparer comparer) where TKey : notnull { } } public static class EnumExtensions From 7737fb9faafc1f25d3cc3ecd1b6b1fd1b0a1118d Mon Sep 17 00:00:00 2001 From: Michael Mutunga Date: Tue, 15 Apr 2025 13:08:56 +0300 Subject: [PATCH 3/3] update dictionary extenstion --- src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs | 4 ++-- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs index 41741f644..30e5aea3a 100644 --- a/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs +++ b/src/Microsoft.OpenApi/Extensions/DictionaryExtensions.cs @@ -13,7 +13,7 @@ public static class DictionaryExtensions /// Returns a new dictionary with entries sorted by key using the default comparer. /// public static SortedDictionary Sort( - this IDictionary source) + this Dictionary source) where TKey : notnull { Utils.CheckArgumentNull(source); @@ -25,7 +25,7 @@ public static SortedDictionary Sort( /// Returns a new dictionary with entries sorted by key using a custom comparer. /// public static SortedDictionary Sort( - this IDictionary source, + this Dictionary source, IComparer comparer) where TKey : notnull { diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index e619fc6c3..38d9b4c59 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -144,9 +144,9 @@ namespace Microsoft.OpenApi.Extensions { public static class DictionaryExtensions { - public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.IDictionary source) + public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.Dictionary source) where TKey : notnull { } - public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.IDictionary source, System.Collections.Generic.IComparer comparer) + public static System.Collections.Generic.SortedDictionary Sort(this System.Collections.Generic.Dictionary source, System.Collections.Generic.IComparer comparer) where TKey : notnull { } } public static class EnumExtensions