diff --git a/csharp/authentication-system/.exercism/config.json b/csharp/authentication-system/.exercism/config.json new file mode 100644 index 0000000..c2e5bdf --- /dev/null +++ b/csharp/authentication-system/.exercism/config.json @@ -0,0 +1,21 @@ +{ + "blurb": "Learn about the const and readonly keywords by refactoring an authentication system.", + "contributors": [ + "ErikSchierboom", + "yzAlvin" + ], + "authors": [ + "mikedamay" + ], + "files": { + "solution": [ + "AuthenticationSystem.cs" + ], + "test": [ + "AuthenticationSystemTests.cs" + ], + "exemplar": [ + ".meta/Exemplar.cs" + ] + } +} diff --git a/csharp/authentication-system/.exercism/metadata.json b/csharp/authentication-system/.exercism/metadata.json new file mode 100644 index 0000000..199946a --- /dev/null +++ b/csharp/authentication-system/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"csharp","exercise":"authentication-system","id":"f06b3c9cc6b24910ae8bb67581d1b6e2","url":"https://exercism.org/tracks/csharp/exercises/authentication-system","handle":"benharri","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/csharp/authentication-system/AuthenticationSystem.cs b/csharp/authentication-system/AuthenticationSystem.cs new file mode 100644 index 0000000..36c624a --- /dev/null +++ b/csharp/authentication-system/AuthenticationSystem.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; + +public class Authenticator +{ + private class EyeColor + { + public string Blue = "blue"; + public string Green = "green"; + public string Brown = "brown"; + public string Hazel = "hazel"; + public string Brey = "grey"; + } + + public Authenticator(Identity admin) + { + this.admin = admin; + } + + private Identity admin; + + private readonly IDictionary developers + = new Dictionary + { + ["Bertrand"] = new Identity + { + Email = "bert@ex.ism", + EyeColor = "blue" + }, + + ["Anders"] = new Identity + { + Email = "anders@ex.ism", + EyeColor = "brown" + } + }; + + public Identity Admin + { + get { return admin; } + } + + public IDictionary GetDevelopers() + { + return developers; + } +} + +public struct Identity +{ + public string Email { get; set; } + + public string EyeColor { get; set; } +} diff --git a/csharp/authentication-system/AuthenticationSystem.csproj b/csharp/authentication-system/AuthenticationSystem.csproj new file mode 100644 index 0000000..d9293a4 --- /dev/null +++ b/csharp/authentication-system/AuthenticationSystem.csproj @@ -0,0 +1,14 @@ + + + + net5.0 + + + + + + + + + + diff --git a/csharp/authentication-system/AuthenticationSystemTests.cs b/csharp/authentication-system/AuthenticationSystemTests.cs new file mode 100644 index 0000000..759d96d --- /dev/null +++ b/csharp/authentication-system/AuthenticationSystemTests.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Xunit; +using Exercism.Tests; + +public class AuthenticationSystemTests +{ + [Fact] + [Task(4)] + public void GetAdmin() + { + var admin = new Identity { EyeColor = "green", Email = "admin@ex.ism" }; + var authenticator = new Authenticator(admin); + Assert.Equal(admin, authenticator.Admin); + } + + [Fact] + [Task(5)] + public void GetDevelopers() + { + var authenticator = new Authenticator(new Identity { EyeColor = "green", Email = "admin@ex.ism" }); + var devs = authenticator.GetDevelopers() as IDictionary; + bool?[] actual = { devs != null, devs?.Count == 2, devs?["Anders"].EyeColor == "brown" }; + bool?[] expected = { true, true, true }; + Assert.Equal(expected, actual); + } +} diff --git a/csharp/authentication-system/HELP.md b/csharp/authentication-system/HELP.md new file mode 100644 index 0000000..27765f3 --- /dev/null +++ b/csharp/authentication-system/HELP.md @@ -0,0 +1,39 @@ +# Help + +## Running the tests + +You can run the tests by opening a command prompt in the exercise's directory, and then running the [`dotnet test` command](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test) +Alternatively, most IDE's have built-in support for running tests, including [Visual Studio](https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer), [Rider](https://www.jetbrains.com/help/rider/Unit_Testing_in_Solution.html) and [Visual Studio code](https://github.com/OmniSharp/omnisharp-vscode/wiki/How-to-run-and-debug-unit-tests). +See the [tests page](https://exercism.io/tracks/csharp/tests) for more information. + +## Skipped tests + +Initially, only the first test will be enabled. +This is to encourage you to solve the exercise one step at a time. +Once you get the first test passing, remove the `Skip` property from the next test and work on getting that test passing. + +## Submitting your solution + +You can submit your solution using the `exercism submit AuthenticationSystem.cs` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [C# track's documentation](https://exercism.org/docs/tracks/csharp) +- [Exercism's support channel on gitter](https://gitter.im/exercism/support) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +To get help if you're having trouble, you can use one of the following resources: + +- [Gitter](https://gitter.im/exercism/xcsharp) is Exercism C# track's Gitter room; go here to get support and ask questions related to the C# track. +- [/r/csharp](https://www.reddit.com/r/csharp) is the C# subreddit. +- [StackOverflow](http://stackoverflow.com/questions/tagged/c%23) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions. \ No newline at end of file diff --git a/csharp/authentication-system/HINTS.md b/csharp/authentication-system/HINTS.md new file mode 100644 index 0000000..4968135 --- /dev/null +++ b/csharp/authentication-system/HINTS.md @@ -0,0 +1,32 @@ +# Hints + +## General + +- [Readonly fields][readonly-fields]: how to define a readonly field. +- [Constants][constants]: how to define constants. + +## 1. Set appropriate fields and properties to const + +- Constants in C# are discussed [here][constants]. + +## 2. Set appropriate fields to readonly + +- Read-only fields are discussed [here][readonly-fields]. + +## 3. Remove set accessor or make it private for any appropriate field + +- This [article][properties] discusses C# properties. + +## 4. Ensure that the admin cannot be tampered with + +- See [this][defensive-copying] discussion on the pattern and purpose of defensive copying. + +## 5. Ensure that the developers cannot be tampered with + +- Read-only dictionaries are discussed [here][readonly-dictionaries]. + +[readonly-fields]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly#readonly-field-example +[constants]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/constants +[properties]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties +[defensive-copying]: https://www.informit.com/articles/article.aspx?p=31551&seqNum=2 +[readonly-dictionaries]: https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.readonlydictionary-2?view=netcore-3.1 \ No newline at end of file diff --git a/csharp/authentication-system/README.md b/csharp/authentication-system/README.md new file mode 100644 index 0000000..f880a76 --- /dev/null +++ b/csharp/authentication-system/README.md @@ -0,0 +1,88 @@ +# Authentication System + +Welcome to Authentication System on Exercism's C# Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +## Constants + +The `const` modifier can be (and generally should be) applied to any field where its value is known at compile time and will not change during the lifetime of the program. + +```csharp +private const int num = 1729; +public const string title = "Grand" + " Master"; +``` + +The `readonly` modifier can be (and generally should be) applied to any field that cannot be made `const` where its value will not change during the lifetime of the program and is either set by an inline initializer or during instantiation (by the constructor or a method called by the constructor). + +```csharp +private readonly int num; +private readonly System.Random rand = new System.Random(); + +public MyClass(int num) +{ + this.num = num; +} +``` + +## Readonly Collections + +While the `readonly` modifier prevents the value or reference in a field from being overwritten, it offers no protection for the members of a reference type. + +```csharp +readonly List ints = new List(); + +void Foo() +{ + ints.Add(1); // ok + ints = new List(); // fails to compile +} +``` + +To ensure that all members of a reference type are protected the fields can be made `readonly` and automatic properties can be defined without a `set` accessor. + +The Base Class Library (BCL) provides some readonly versions of collections where there is a requirement to stop members of a collections being updated. These come in the form of wrappers: + +- `ReadOnlyDictionary` exposes a `Dictionary` as read-only. +- `ReadOnlyCollection` exposes a `List` as read-only. + +## Defensive Copying + +In security sensitive situations (or even simply on a large code-base where developers have different priorities and agendas) you should avoid allowing a class's public API to be circumvented by accepting and storing a method's mutable parameters or by exposing a mutable member of a class through a return value or as an `out` parameter. + +## Instructions + +The authentication system that you last saw in [exercise:csharp/developer-privileges]() is in need of some attention. You have been tasked with cleaning up the code. Such a cleanup project will not only make life easy for future maintainers but will expose and fix some security vulnerabilities. + +## 1. Set appropriate fields and properties to const + +This is a refactoring task. Add the `const` modifier to any members of `Authenticator` or `Identity` that you think appropriate. + +## 2. Set appropriate fields to readonly + +This is a refactoring task. Add the `readonly` modifier to any fields of the `Authenticator` class or the `Identity` struct that you think appropriate. + +## 3. Ensure that the class cannot be changed once it has been created + +Remove the `set` accessor or make it `private` for any appropriate property on the `Authenticator` class or `Identity` struct. + +## 4. Ensure that the admin cannot be tampered with + +At present the admin identity field is returned by a call to `Admin`. This is not ideal as the caller can modify the field. Find a way to prevent the caller modifying the details of admin on the `Authenticator` object. + +## 5. Ensure that the developers cannot be tampered with + +At present the dictionary containing the hard coded privileged developer identities is returned by a call to `GetDevelopers()`. This is not ideal as the caller can modify the dictionary. Find a way to prevent the caller modifying the details of admin on the `Authenticator` object. + +## Source + +### Created by + +- @mikedamay + +### Contributed to by + +- @ErikSchierboom +- @yzAlvin \ No newline at end of file diff --git a/csharp/international-calling-connoisseur/.exercism/config.json b/csharp/international-calling-connoisseur/.exercism/config.json new file mode 100644 index 0000000..9f71a99 --- /dev/null +++ b/csharp/international-calling-connoisseur/.exercism/config.json @@ -0,0 +1,22 @@ +{ + "blurb": "Learn about dictionaries by keeping track of international dialling codes.", + "contributors": [ + "ErikSchierboom", + "valentin-p", + "yzAlvin" + ], + "authors": [ + "mikedamay" + ], + "files": { + "solution": [ + "InternationalCallingConnoisseur.cs" + ], + "test": [ + "InternationalCallingConnoisseurTests.cs" + ], + "exemplar": [ + ".meta/Exemplar.cs" + ] + } +} diff --git a/csharp/international-calling-connoisseur/.exercism/metadata.json b/csharp/international-calling-connoisseur/.exercism/metadata.json new file mode 100644 index 0000000..d10c33b --- /dev/null +++ b/csharp/international-calling-connoisseur/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"csharp","exercise":"international-calling-connoisseur","id":"03a5da35918c4defa48b9db20aa17c3f","url":"https://exercism.org/tracks/csharp/exercises/international-calling-connoisseur","handle":"benharri","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/csharp/international-calling-connoisseur/HELP.md b/csharp/international-calling-connoisseur/HELP.md new file mode 100644 index 0000000..12748ac --- /dev/null +++ b/csharp/international-calling-connoisseur/HELP.md @@ -0,0 +1,39 @@ +# Help + +## Running the tests + +You can run the tests by opening a command prompt in the exercise's directory, and then running the [`dotnet test` command](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test) +Alternatively, most IDE's have built-in support for running tests, including [Visual Studio](https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer), [Rider](https://www.jetbrains.com/help/rider/Unit_Testing_in_Solution.html) and [Visual Studio code](https://github.com/OmniSharp/omnisharp-vscode/wiki/How-to-run-and-debug-unit-tests). +See the [tests page](https://exercism.io/tracks/csharp/tests) for more information. + +## Skipped tests + +Initially, only the first test will be enabled. +This is to encourage you to solve the exercise one step at a time. +Once you get the first test passing, remove the `Skip` property from the next test and work on getting that test passing. + +## Submitting your solution + +You can submit your solution using the `exercism submit InternationalCallingConnoisseur.cs` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [C# track's documentation](https://exercism.org/docs/tracks/csharp) +- [Exercism's support channel on gitter](https://gitter.im/exercism/support) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +To get help if you're having trouble, you can use one of the following resources: + +- [Gitter](https://gitter.im/exercism/xcsharp) is Exercism C# track's Gitter room; go here to get support and ask questions related to the C# track. +- [/r/csharp](https://www.reddit.com/r/csharp) is the C# subreddit. +- [StackOverflow](http://stackoverflow.com/questions/tagged/c%23) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions. \ No newline at end of file diff --git a/csharp/international-calling-connoisseur/HINTS.md b/csharp/international-calling-connoisseur/HINTS.md new file mode 100644 index 0000000..82aed72 --- /dev/null +++ b/csharp/international-calling-connoisseur/HINTS.md @@ -0,0 +1,56 @@ +# Hints + +## 1. Create a new dictionary + +A dictionary is like any other class. You simply 'new' it to create an empty instance. + +## 2. Create a pre-populated dictionary + +Although it's possible to populate a dictionary by repeatedly adding items, dictionaries can be initialized statically. + +See [this article][dictionary_static_initialization]. + +## 3. Add a country to an empty dictionary + +See [Add][dictionary_add]. Pass in the dictionary returned by task 1 as a parameter. + +## 4. Add a country to an existing dictionary + +There is no substantial difference between adding an item to an empty or initialized dictionary. Pass in the dictionary returned by task 2 as a parameter. + +## 5. Get the country name matching a country Code + +See [this article][dictionary_item]. + +## 6. Attempt to get country name for a non-existent country code + +You need to [detect][dictionary_contains_key] whether the country is present in the dictionary. + +## 7. Attempt to get country name for a non-existent country code + +You can combine what you've learnt in Tasks 5 and 6 to solve this one. + +## 8. Update a country name + +Again [this article][dictionary_item] applies. + +## 9. Attempt to ypdate name of country that is not in the dictionary + +This is very similar to task 7. + +## 10. Remove a country from the dictionary + +See [this article][dictionary_remove]. + +## 11. Find the country with the longest name + +See the [values collection][dictionary_values], [string length][string_length] and [foreach][foreach]. + +[dictionary_static_initialization]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-initialize-a-dictionary-with-a-collection-initializer +[dictionary_add]: https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.add?view=netcore-3.1 +[dictionary_item]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.item?view=netcore-3.1 +[dictionary_contains_key]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.containskey?view=netcore-3.1 +[dictionary_remove]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.remove?view=netcore-3.1 +[dictionary_values]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.values?view=netcore-3.1 +[foreach]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in +[string_length]: https://docs.microsoft.com/en-gb/dotnet/api/system.string.length?view=netcore-3.1#System_String_Length \ No newline at end of file diff --git a/csharp/international-calling-connoisseur/InternationalCallingConnoisseur.cs b/csharp/international-calling-connoisseur/InternationalCallingConnoisseur.cs new file mode 100644 index 0000000..854133f --- /dev/null +++ b/csharp/international-calling-connoisseur/InternationalCallingConnoisseur.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +public static class DialingCodes +{ + public static Dictionary GetEmptyDictionary() => new(); + + public static Dictionary GetExistingDictionary() => + new() + { + { 1, "United States of America" }, + { 55, "Brazil" }, + { 91, "India" } + }; + + public static Dictionary AddCountryToEmptyDictionary(int countryCode, string countryName) => + new() + { + { countryCode, countryName } + }; + + public static Dictionary AddCountryToExistingDictionary( + Dictionary existingDictionary, int countryCode, string CountryName) + { + existingDictionary.Add(countryCode, CountryName); + return existingDictionary; + } + + public static string GetCountryNameFromDictionary( + Dictionary existingDictionary, int countryCode) => + existingDictionary.GetValueOrDefault(countryCode, string.Empty); + + public static Dictionary UpdateDictionary( + Dictionary existingDictionary, int countryCode, string countryName) + { + if (existingDictionary.ContainsKey(countryCode)) + existingDictionary[countryCode] = countryName; + return existingDictionary; + } + + public static Dictionary RemoveCountryFromDictionary( + Dictionary existingDictionary, int countryCode) + { + existingDictionary.Remove(countryCode); + return existingDictionary; + } + + public static bool CheckCodeExists(Dictionary existingDictionary, int countryCode) => + existingDictionary.ContainsKey(countryCode); + + public static string FindLongestCountryName(Dictionary existingDictionary) => + existingDictionary.Values.OrderByDescending(n => n.Length).FirstOrDefault() ?? string.Empty; +} diff --git a/csharp/international-calling-connoisseur/InternationalCallingConnoisseur.csproj b/csharp/international-calling-connoisseur/InternationalCallingConnoisseur.csproj new file mode 100644 index 0000000..d9293a4 --- /dev/null +++ b/csharp/international-calling-connoisseur/InternationalCallingConnoisseur.csproj @@ -0,0 +1,14 @@ + + + + net5.0 + + + + + + + + + + diff --git a/csharp/international-calling-connoisseur/InternationalCallingConnoisseurTests.cs b/csharp/international-calling-connoisseur/InternationalCallingConnoisseurTests.cs new file mode 100644 index 0000000..57caec3 --- /dev/null +++ b/csharp/international-calling-connoisseur/InternationalCallingConnoisseurTests.cs @@ -0,0 +1,295 @@ +using Xunit; +using Exercism.Tests; + +public class InternationalCallingConnoisseurTests +{ + [Fact] + [Task(1)] + public void Empty_dictionary_is_empty() + { + var emptyDict = DialingCodes.GetEmptyDictionary(); + Assert.Empty(emptyDict); + } + + [Fact] + [Task(2)] + public void Existing_dictionary_count_is_3() + { + var prePopulated = DialingCodes.GetExistingDictionary(); + Assert.Equal(3, prePopulated.Count); + } + + [Fact] + [Task(2)] + public void Existing_dictionary_1_is_United_States_of_America() + { + var prePopulated = DialingCodes.GetExistingDictionary(); + Assert.Equal("United States of America", prePopulated[1]); + } + + [Fact] + [Task(2)] + public void Existing_dictionary_55_is_Brazil() + { + var prePopulated = DialingCodes.GetExistingDictionary(); + Assert.Equal("Brazil", prePopulated[55]); + } + + [Fact] + [Task(2)] + public void Existing_dictionary_91_is_India() + { + var prePopulated = DialingCodes.GetExistingDictionary(); + Assert.Equal("India", prePopulated[91]); + } + + [Fact] + [Task(3)] + public void Add_country_to_empty_dictionary_single() + { + var countryCodes = DialingCodes.AddCountryToEmptyDictionary(44, "United Kingdom"); + Assert.Single(countryCodes); + } + + [Fact] + [Task(3)] + public void Add_country_to_empty_dictionary_44_is_United_Kingdom() + { + var countryCodes = DialingCodes.AddCountryToEmptyDictionary(44, "United Kingdom"); + Assert.Equal("United Kingdom", countryCodes[44]); + } + + [Fact] + [Task(4)] + public void Add_country_to_existing_dictionary_count_is_1() + { + var countryCodes = DialingCodes.AddCountryToExistingDictionary( + DialingCodes.GetExistingDictionary(), 44, "United Kingdom"); + Assert.Equal(4, countryCodes.Count); + } + + [Fact] + [Task(4)] + public void Add_country_to_existing_dictionary_1_is_United_States_of_America() + { + var countryCodes = DialingCodes.AddCountryToExistingDictionary( + DialingCodes.GetExistingDictionary(), 44, "United Kingdom"); + Assert.Equal("United States of America", countryCodes[1]); + } + + [Fact] + [Task(4)] + public void Add_country_to_existing_dictionary_44_is_United_Kingdom() + { + var countryCodes = DialingCodes.AddCountryToExistingDictionary( + DialingCodes.GetExistingDictionary(), 44, "United Kingdom"); + Assert.Equal("United Kingdom", countryCodes[44]); + } + + [Fact] + [Task(4)] + public void Add_country_to_existing_dictionary_55_is_Brazil() + { + var countryCodes = DialingCodes.AddCountryToExistingDictionary( + DialingCodes.GetExistingDictionary(), 44, "United Kingdom"); + Assert.Equal("Brazil", countryCodes[55]); + } + + [Fact] + [Task(4)] + public void Add_country_to_existing_dictionary_91_is_India() + { + var countryCodes = DialingCodes.AddCountryToExistingDictionary( + DialingCodes.GetExistingDictionary(), 44, "United Kingdom"); + Assert.Equal("India", countryCodes[91]); + } + + [Fact] + [Task(5)] + public void Get_country_name_from_dictionary() + { + var countryName = DialingCodes.GetCountryNameFromDictionary( + DialingCodes.GetExistingDictionary(), 55); + Assert.Equal("Brazil", countryName); + } + + [Fact] + [Task(5)] + public void Get_country_name_for_non_existent_country() + { + var countryName = DialingCodes.GetCountryNameFromDictionary( + DialingCodes.GetExistingDictionary(), 999); + Assert.Equal(string.Empty, countryName); + } + + [Fact] + [Task(6)] + public void Check_country_exists() + { + var exists = DialingCodes.CheckCodeExists( + DialingCodes.GetExistingDictionary(), 55); + Assert.True(exists); + } + + [Fact] + [Task(6)] + public void Check_country_exists_for_non_existent_country() + { + var exists = DialingCodes.CheckCodeExists( + DialingCodes.GetExistingDictionary(), 999); + Assert.False(exists); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_count_is_3() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 1, "les États-Unis"); + Assert.Equal(3, countryCodes.Count); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_1_is_les_Etats_Unis() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 1, "les États-Unis"); + Assert.Equal("les États-Unis", countryCodes[1]); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_55_is_Brazil() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 1, "les États-Unis"); + Assert.Equal("Brazil", countryCodes[55]); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_91_is_India() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 1, "les États-Unis"); + Assert.Equal("India", countryCodes[91]); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_for_non_existent_country_count_is_3() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 999, "Newlands"); + Assert.Equal(3, countryCodes.Count); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_for_non_existent_country_1_is_United_States_of_America() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 999, "Newlands"); + Assert.Equal("United States of America", countryCodes[1]); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_for_non_existent_country_55_is_Brazil() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 999, "Newlands"); + Assert.Equal("Brazil", countryCodes[55]); + } + + [Fact] + [Task(7)] + public void Update_country_name_in_dictionary_for_non_existent_country_91_is_India() + { + var countryCodes = DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 999, "Newlands"); + Assert.Equal("India", countryCodes[91]); + } + + [Fact] + [Task(8)] + public void Remove_country_from_dictionary_count_is_2() + { + var countryCodes = DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 91); + Assert.Equal(2, countryCodes.Count); + } + + [Fact] + [Task(8)] + public void Remove_country_from_dictionary_1_is_United_States_of_America() + { + var countryCodes = DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 91); + Assert.Equal("United States of America", countryCodes[1]); + } + + [Fact] + [Task(8)] + public void Remove_country_from_dictionary_55_is_Brazil() + { + var countryCodes = DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 91); + Assert.Equal("Brazil", countryCodes[55]); + } + + [Fact] + [Task(8)] + public void Remove_country_from_dictionary_for_non_existent_country_count_is_3() + { + var countryCodes = DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 999); + Assert.Equal(3, countryCodes.Count); + } + + [Fact] + [Task(8)] + public void Remove_country_from_dictionary_for_non_existent_country_1_is_United_States_of_America() + { + var countryCodes = DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 999); + Assert.Equal("United States of America", countryCodes[1]); + } + + [Fact] + [Task(8)] + public void Remove_country_from_dictionary_for_non_existent_country_55_is_Brazil() + { + var countryCodes = DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 999); + Assert.Equal("Brazil", countryCodes[55]); + } + + [Fact] + [Task(8)] + public void Remove_country_from_dictionary_for_non_existent_country_91_is_India() + { + var countryCodes = DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 999); + Assert.Equal("India", countryCodes[91]); + } + + [Fact] + [Task(9)] + public void Longest_country_name() + { + var longestCountryName = DialingCodes.FindLongestCountryName( + DialingCodes.GetExistingDictionary()); + Assert.Equal("United States of America", longestCountryName); + } + + [Fact] + [Task(9)] + public void Longest_country_name_for_empty_dictionary() + { + var longestCountryName = DialingCodes.FindLongestCountryName( + DialingCodes.GetEmptyDictionary()); + Assert.Equal(string.Empty, longestCountryName); + } +} diff --git a/csharp/international-calling-connoisseur/README.md b/csharp/international-calling-connoisseur/README.md new file mode 100644 index 0000000..7898fa4 --- /dev/null +++ b/csharp/international-calling-connoisseur/README.md @@ -0,0 +1,182 @@ +# International Calling Connoisseur + +Welcome to International Calling Connoisseur on Exercism's C# Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +A dictionary is a collection of elements where each element comprises a key and value such that if a key is passed to a method of the dictionary its associated value is returned. It has the same role as maps or associative arrays do in other languages. + +A dictionary can be created as follows: + +```csharp +new Dictionary(); +// Empty dictionary +``` + +Or + +```csharp +new Dictionary +{ + [1] = "One", + [2] = "Two" +}; + +// Or + +new Dictionary +{ + {1, "One"}, + {2, "Two"} +}; +// 1 => "One", 2 => "Two" +``` + +Note that the key and value types are part of the definition of the dictionary. + +Once constructed, entries can be added or removed from a dictionary using its built-in methods `Add` and `Remove`. + +Retrieving or updating values in a dictionary is done by indexing into the dictionary using a key: + +```csharp +var numbers = new Dictionary +{ + {1, "One"}, + {2, "Two"} +}; + +// Set the value of the element with key 2 to "Deux" +numbers[2] = "Deux"; + +// Get the value of the element with key 2 +numbers[2]; +// => "Deux" +``` + +You can test if a value exists in the dictionary with: + +```csharp +var dict = new Dictionary{/*...*/}; +dict.ContainsKey("some key that exists"); +// => true +``` + +Enumerating over a dictionary will enumerate over its key/value pairs. Dictionaries also have properties that allow enumerating over its keys or values. + +[indexer-properties]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/ + +## Instructions + +In this exercise you'll be writing code to keep track of international dialling codes via an international dialing code dictionary. + +The dictionary uses an integer for its keys (the dialing code) and a string (country name) for its values. + +You have 9 tasks which involve the `DialingCodes` static class. + +## 1. Create a new dictionary + +Implement the (static) method `DialingCodes.GetEmptyDictionary()` that returns an empty dictionary. + +```csharp +DialingCodes.GetEmptyDictionary(); +// => empty dictionary +``` + +## 2. Create a pre-populated dictionary + +There exists a pre-populated dictionary which contains the following 3 dialing codes: "United States of America" which has a code of 1, "Brazil" which has a code of 55 and "India" which has a code of 91. Implement the (static) `DialingCodes.GetExistingDictionary()` method to return the pre-populated dictionary: + +```csharp +DialingCodes.GetExistingDictionary(); +// => 1 => "United States of America", 55 => "Brazil", 91 => "India" +``` + +## 3. Add a country to an empty dictionary + +Implement the (static) method `DialingCodes.AddCountryToEmptyDictionary()` that creates a dictionary and adds a dialing code and associated country name to it. + +```csharp +DialingCodes.AddCountryToEmptyDictionary(44, "United Kingdom"); +// => 44 => "United Kingdom" +``` + +## 4. Add a country to an existing dictionary + +Implement the (static) method `DialingCodes.AddCountryToExistingDictionary()` that adds a dialing code and associated country name to a non-empty dictionary. + +```csharp +DialingCodes.AddCountryToExistingDictionary(DialingCodes.GetExistingDictionary(), + 44, "United Kingdom"); +// => 1 => "United States of America", 44 => "United Kingdom", 55 => "Brazil", 91 => "India" +``` + +## 5. Get the country name matching a dialing code + +Implement the (static) method `DialingCodes.GetCountryNameFromDictionary()` that takes a dialing code and returns the corresponding country name. If the dialing code is not contained in the dictionary then an empty string is returned. + +```csharp +DialingCodes.GetCountryNameFromDictionary( + DialingCodes.GetExistingDictionary(), 55); +// => "Brazil" + +DialingCodes.GetCountryNameFromDictionary( + DialingCodes.GetExistingDictionary(), 999); +// => string.Empty +``` + +## 6. Check that a country exists in the dictionary + +Implement the (static) method `DialingCodes.CheckCodeExists()` to check whether a dialing code exists in the dictionary. + +```csharp +DialingCodes.CheckCodeExists(DialingCodes.GetExistingDictionary(), 55); +// => true +``` + +## 7. Update a country name + +Implement the (static) method `DialingCodes.UpdateDictionary()` which takes a dialing code and replaces the corresponding country name in the dictionary with the country name passed as a parameter. If the dialing code does not exist in the dictionary then the dictionary remains unchanged. + +```csharp +DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 1, "Les États-Unis"); +// => 1 => "Les États-Unis", 55 => "Brazil", 91 => "India" + +DialingCodes.UpdateDictionary( + DialingCodes.GetExistingDictionary(), 999, "Newlands"); +// 1 => "United States of America", 55 => "Brazil", 91 => "India" +``` + +## 8. Remove a country from the dictionary + +Implement the (static) method `DialingCodes.RemoveCountryFromDictionary()` that takes a dialing code and will remove the corresponding record, dialing code + country name, from the dictionary. + +```csharp +DialingCodes.RemoveCountryFromDictionary( + DialingCodes.GetExistingDictionary(), 91); +// => 1 => "United States of America", 55 => "Brazil" +``` + +## 9. Find the country with the longest name + +Implement the (static) method `DialingCodes.FindLongestCountryName()` which will return the name of the country with the longest name stored in the dictionary. + +```csharp +DialingCodes.FindLongestCountryName( + DialingCodes.GetExistingDictionary()); +// => "United States of America" +``` + +## Source + +### Created by + +- @mikedamay + +### Contributed to by + +- @ErikSchierboom +- @valentin-p +- @yzAlvin \ No newline at end of file