diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 56a0c62f8..79f42dc1d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,7 +16,7 @@ variables: steps: - task: DotNetCoreInstaller@0 inputs: - version: '2.1.500' + version: '2.2.105' - task: DotNetCoreCLI@2 inputs: diff --git a/src/Directory.build.props b/src/Directory.build.props index 84d7166db..f95fea5f9 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -7,7 +7,7 @@ - + diff --git a/src/Humanizer.Tests.Shared/ArticlePrefixSortTests.cs b/src/Humanizer.Tests.Shared/ArticlePrefixSortTests.cs new file mode 100644 index 000000000..f2d3e1450 --- /dev/null +++ b/src/Humanizer.Tests.Shared/ArticlePrefixSortTests.cs @@ -0,0 +1,23 @@ +using System; +using Xunit; + +namespace Humanizer.Tests +{ + public class ArticlePrefixSortTests + { + [Theory] + [InlineData(new[] { "Ant", "The Theater", "The apple", "Fox", "Bear" }, new[] { "Ant", "The apple", "Bear", "Fox", "The Theater" })] + public void SortStringArrayIgnoringArticlePrefixes(string[] input, string[] expectedOutput) + { + Assert.Equal(expectedOutput, EnglishArticle.PrependArticleSuffix(EnglishArticle.AppendArticlePrefix(input))); + } + + [Fact] + public void An_Empty_String_Array_Throws_ArgumentOutOfRangeException() + { + string[] items = { }; + void action() => EnglishArticle.AppendArticlePrefix(items); + Assert.Throws(action); + } + } +} diff --git a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt index 005aac23c..e073cc484 100644 --- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt +++ b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt @@ -59,6 +59,16 @@ namespace Humanizer.Bytes public static Humanizer.Bytes.ByteSize FromMegabytes(double value) { } public static Humanizer.Bytes.ByteSize FromTerabytes(double value) { } public override int GetHashCode() { } + public static Humanizer.Bytes.ByteSize +(Humanizer.Bytes.ByteSize b1, Humanizer.Bytes.ByteSize b2) { } + public static Humanizer.Bytes.ByteSize --(Humanizer.Bytes.ByteSize b) { } + public static bool ==(Humanizer.Bytes.ByteSize b1, Humanizer.Bytes.ByteSize b2) { } + public static bool >(Humanizer.Bytes.ByteSize b1, Humanizer.Bytes.ByteSize b2) { } + public static bool >=(Humanizer.Bytes.ByteSize b1, Humanizer.Bytes.ByteSize b2) { } + public static Humanizer.Bytes.ByteSize ++(Humanizer.Bytes.ByteSize b) { } + public static bool !=(Humanizer.Bytes.ByteSize b1, Humanizer.Bytes.ByteSize b2) { } + public static bool <(Humanizer.Bytes.ByteSize b1, Humanizer.Bytes.ByteSize b2) { } + public static bool <=(Humanizer.Bytes.ByteSize b1, Humanizer.Bytes.ByteSize b2) { } + public static Humanizer.Bytes.ByteSize -(Humanizer.Bytes.ByteSize b) { } public static Humanizer.Bytes.ByteSize Parse(string s) { } public Humanizer.Bytes.ByteSize Subtract(Humanizer.Bytes.ByteSize bs) { } public string ToFullWords() { } @@ -150,6 +160,17 @@ namespace Humanizer public static string ToOrdinalWords(this System.DateTime input) { } public static string ToOrdinalWords(this System.DateTime input, Humanizer.GrammaticalCase grammaticalCase) { } } + public class static EnglishArticle + { + public static string[] AppendArticlePrefix(string[] items) { } + public static string[] PrependArticleSuffix(string[] appended) { } + } + public enum EnglishArticles + { + A = 0, + An = 1, + The = 2, + } public class static EnumDehumanizeExtensions { public static TTargetEnum DehumanizeTo(this string input) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index e8e2a266a..385809766 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -7,16 +7,16 @@ $(DefineConstants);NETFX_CORE - - - + + + - - - + + + diff --git a/src/Humanizer.sln b/src/Humanizer.sln index 05683f108..524773fff 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28711.165 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Humanizer.Tests", "Humanizer.Tests\Humanizer.Tests.csproj", "{F886A8DA-3EFC-4A89-91DD-06FAF13DA172}" EndProject @@ -12,18 +12,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\.editorconfig = ..\.editorconfig ..\.gitattributes = ..\.gitattributes ..\.gitignore = ..\.gitignore + Directory.build.props = Directory.build.props ..\NuSpecs\Humanizer.Core.nuspec = ..\NuSpecs\Humanizer.Core.nuspec ..\NuSpecs\Humanizer.nuspec = ..\NuSpecs\Humanizer.nuspec ..\LICENSE = ..\LICENSE ..\readme.md = ..\readme.md ..\release_notes.md = ..\release_notes.md + ..\version.json = ..\version.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{97AAE24D-0488-42AE-A585-86D882F23D5F}" ProjectSection(SolutionItems) = preProject - ..\.vsts-ci.yml = ..\.vsts-ci.yml - ..\.vsts-pr.yml = ..\.vsts-pr.yml - ..\.vsts-shared.yml = ..\.vsts-shared.yml + ..\azure-pipelines.yml = ..\azure-pipelines.yml ..\build.cmd = ..\build.cmd Humanizer.ruleset = Humanizer.ruleset EndProjectSection diff --git a/src/Humanizer/ArticlePrefixSort.cs b/src/Humanizer/ArticlePrefixSort.cs new file mode 100644 index 000000000..961792624 --- /dev/null +++ b/src/Humanizer/ArticlePrefixSort.cs @@ -0,0 +1,108 @@ +using System; +using System.Text.RegularExpressions; + +namespace Humanizer +{ + /// + /// Contains methods for removing, appending and prepending article prefixes for sorting strings ignoring the article. + /// + public static class EnglishArticle + { + /// + /// Removes the prefixed article and appends it to the same string. + /// + /// The input array of strings + /// Sorted string array + public static string[] AppendArticlePrefix(string[] items) + { + if (items.Length == 0) + { + throw new ArgumentOutOfRangeException(nameof(items)); + } + + var regex = new Regex("^((The)|(the)|(a)|(A)|(An)|(an))\\s\\w+"); + var transformed = new string[items.Length]; + + for (var i = 0; i < items.Length; i++) + { + if (regex.IsMatch(items[i])) + { + var article = items[i].Substring(0, items[i].IndexOf(" ", StringComparison.CurrentCulture)); + var removed = items[i].Remove(0, items[i].IndexOf(" ", StringComparison.CurrentCulture)); + var appended = $"{removed} {article}"; + transformed[i] = appended.Trim(); + } + else + { + transformed[i] = items[i].Trim(); + } + } + Array.Sort(transformed); + return transformed; + } + + /// + /// Removes the previously appended article and prepends it to the same string. + /// + /// Sorted string array + /// String array + public static string[] PrependArticleSuffix(string[] appended) + { + var inserted = new string[appended.Length]; + + for (var i = 0; i < appended.Length; i++) + { + string suffix; + string original; + if (appended[i].EndsWith(EnglishArticles.The.ToString())) + { + suffix = appended[i].Substring(appended[i].IndexOf(" The", StringComparison.CurrentCulture)); + original = ToOriginalFormat(appended, suffix, i); + inserted[i] = original; + } + else if (appended[i].EndsWith(EnglishArticles.A.ToString())) + { + suffix = appended[i].Substring(appended[i].IndexOf(" A", StringComparison.CurrentCulture)); + original = ToOriginalFormat(appended, suffix, i); + inserted[i] = original; + } + else if (appended[i].EndsWith(EnglishArticles.An.ToString())) + { + suffix = appended[i].Substring(appended[i].IndexOf(" An", StringComparison.CurrentCulture)); + original = ToOriginalFormat(appended, suffix, i); + inserted[i] = original; + } + else if (appended[i].EndsWith(EnglishArticles.A.ToString().ToLowerInvariant())) + { + suffix = appended[i].Substring(appended[i].IndexOf(" a", StringComparison.CurrentCulture)); + original = ToOriginalFormat(appended, suffix, i); + inserted[i] = original; + } + else if (appended[i].EndsWith(EnglishArticles.An.ToString().ToLowerInvariant())) + { + suffix = appended[i].Substring(appended[i].IndexOf(" an", StringComparison.CurrentCulture)); + original = ToOriginalFormat(appended, suffix, i); + inserted[i] = original; + } + else if (appended[i].EndsWith(EnglishArticles.The.ToString().ToLowerInvariant())) + { + suffix = appended[i].Substring(appended[i].IndexOf(" the", StringComparison.CurrentCulture)); + original = ToOriginalFormat(appended, suffix, i); + inserted[i] = original; + } + else + { + inserted[i] = appended[i]; + } + } + return inserted; + } + + private static string ToOriginalFormat(string[] appended, string suffix, int i) + { + var insertion = appended[i].Remove(appended[i].IndexOf(suffix, StringComparison.CurrentCulture)); + var original = $"{suffix} {insertion}"; + return original.Trim(); + } + } +} diff --git a/src/Humanizer/Articles.cs b/src/Humanizer/Articles.cs new file mode 100644 index 000000000..290e67378 --- /dev/null +++ b/src/Humanizer/Articles.cs @@ -0,0 +1,21 @@ +namespace Humanizer +{ + /// + /// Definite and Indefinite English Articles + /// + public enum EnglishArticles + { + /// + /// A + /// + A, + /// + /// An + /// + An, + /// + /// The + /// + The + } +} diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 2e9a2bfc3..1e45dcc4c 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -6,7 +6,7 @@ https://github.com/Humanizr/Humanizer 2.12 A micro-framework that turns your normal strings, type names, enum fields, date fields ETC into a human friendly format - Copyright © 2012-2018 Mehdi Khalili + Copyright © 2012-2019 .NET Foundation and Contributors Humanizer ($(TargetFramework)) true true diff --git a/version.json b/version.json index be15d5463..c5578c950 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "2.5", + "version": "2.6", "publicReleaseRefSpec": [ "^refs/heads/master$", // we release out of master "^refs/heads/rel/v\\d+\\.\\d+" // we also release branches starting with rel/vN.N