From 3283134f4c8798172ccdebb6dc7181d1a6dd6e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Thu, 10 Oct 2024 05:51:12 -0700 Subject: [PATCH] Add `nucleotide-count` (#588) * Add `nucleotide-count` * Break up long line * Switch from errors to exceptions * Instructions append * Add info about Exception interface --- config.json | 8 ++++ .../.docs/instructions.append.md | 14 +++++++ .../nucleotide-count/.docs/instructions.md | 23 +++++++++++ .../nucleotide-count/.meta/config.json | 19 +++++++++ .../nucleotide-count/.meta/lib/example.dart | 17 ++++++++ .../nucleotide-count/.meta/tests.toml | 25 +++++++++++ .../nucleotide-count/analysis_options.yaml | 18 ++++++++ .../lib/nucleotide_count.dart | 5 +++ .../practice/nucleotide-count/pubspec.yaml | 5 +++ .../test/nucleotide_count_test.dart | 41 +++++++++++++++++++ 10 files changed, 175 insertions(+) create mode 100644 exercises/practice/nucleotide-count/.docs/instructions.append.md create mode 100644 exercises/practice/nucleotide-count/.docs/instructions.md create mode 100644 exercises/practice/nucleotide-count/.meta/config.json create mode 100644 exercises/practice/nucleotide-count/.meta/lib/example.dart create mode 100644 exercises/practice/nucleotide-count/.meta/tests.toml create mode 100644 exercises/practice/nucleotide-count/analysis_options.yaml create mode 100644 exercises/practice/nucleotide-count/lib/nucleotide_count.dart create mode 100644 exercises/practice/nucleotide-count/pubspec.yaml create mode 100644 exercises/practice/nucleotide-count/test/nucleotide_count_test.dart diff --git a/config.json b/config.json index 7e0161b2..d105c46d 100644 --- a/config.json +++ b/config.json @@ -188,6 +188,14 @@ "math" ] }, + { + "slug": "nucleotide-count", + "name": "Nucleotide Count", + "uuid": "06ea660b-3869-46df-8fd3-a0651b66ed54", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, { "slug": "proverb", "name": "Proverb", diff --git a/exercises/practice/nucleotide-count/.docs/instructions.append.md b/exercises/practice/nucleotide-count/.docs/instructions.append.md new file mode 100644 index 00000000..ea6f010d --- /dev/null +++ b/exercises/practice/nucleotide-count/.docs/instructions.append.md @@ -0,0 +1,14 @@ +# Instructions append + +There are times when you need to handle unexpected issues that arise during code execution. +In Dart, you can use exceptions to handle these situations. + +To throw an exception in Dart, use the `throw` keyword followed by an instance of the specific exception you want to raise. +For example, `throw Exception()` throws an instance of the base Exception type. + +In this exercise, you need to define a custom exception called `InvalidNucleotideException`. +This exception should be raised when an invalid nucleotide is encountered. +The test suite only checks that an instance of `InvalidNucleotideException` is passed, so you have the freedom to implement it as you see fit as long as it is correctly thrown. + +For more information on exceptions in Dart, you can refer to the [exceptions documentation](https://dart.dev/language/error-handling). +Custom exceptions can be created by [implementing the Exception interface](https://dart.dev/language#interfaces-and-abstract-classes). diff --git a/exercises/practice/nucleotide-count/.docs/instructions.md b/exercises/practice/nucleotide-count/.docs/instructions.md new file mode 100644 index 00000000..548d9ba5 --- /dev/null +++ b/exercises/practice/nucleotide-count/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed. +All known life depends on DNA! + +> Note: You do not need to understand anything about nucleotides or DNA to complete this exercise. + +DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine. +A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important! +We call the order of these nucleotides in a bit of DNA a "DNA sequence". + +We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as "ATTACG" for a DNA sequence of 6 nucleotides. +'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' for thymine. + +Given a string representing a DNA sequence, count how many of each nucleotide is present. +If the string contains characters that aren't A, C, G, or T then it is invalid and you should signal an error. + +For example: + +```text +"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2 +"INVALID" -> error +``` diff --git a/exercises/practice/nucleotide-count/.meta/config.json b/exercises/practice/nucleotide-count/.meta/config.json new file mode 100644 index 00000000..298928e7 --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "lib/nucleotide_count.dart" + ], + "test": [ + "test/nucleotide_count_test.dart" + ], + "example": [ + ".meta/lib/example.dart" + ] + }, + "blurb": "Given a DNA string, compute how many times each nucleotide occurs in the string.", + "source": "The Calculating DNA Nucleotides_problem at Rosalind", + "source_url": "https://rosalind.info/problems/dna/" +} diff --git a/exercises/practice/nucleotide-count/.meta/lib/example.dart b/exercises/practice/nucleotide-count/.meta/lib/example.dart new file mode 100644 index 00000000..60d22b38 --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/lib/example.dart @@ -0,0 +1,17 @@ +class InvalidNucleotideException implements Exception {} + +class NucleotideCount { + Map count(String strand) { + final result = {"A": 0, "C": 0, "G": 0, "T": 0}; + + for (final nucleotide in strand.split('')) { + if (!result.containsKey(nucleotide)) { + throw InvalidNucleotideException(); + } + + result[nucleotide] = result[nucleotide]! + 1; + } + + return result; + } +} diff --git a/exercises/practice/nucleotide-count/.meta/tests.toml b/exercises/practice/nucleotide-count/.meta/tests.toml new file mode 100644 index 00000000..7c55e53f --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/tests.toml @@ -0,0 +1,25 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[3e5c30a8-87e2-4845-a815-a49671ade970] +description = "empty strand" + +[a0ea42a6-06d9-4ac6-828c-7ccaccf98fec] +description = "can count one nucleotide in single-character input" + +[eca0d565-ed8c-43e7-9033-6cefbf5115b5] +description = "strand with repeated nucleotide" + +[40a45eac-c83f-4740-901a-20b22d15a39f] +description = "strand with multiple nucleotides" + +[b4c47851-ee9e-4b0a-be70-a86e343bd851] +description = "strand with invalid nucleotides" diff --git a/exercises/practice/nucleotide-count/analysis_options.yaml b/exercises/practice/nucleotide-count/analysis_options.yaml new file mode 100644 index 00000000..c06363d6 --- /dev/null +++ b/exercises/practice/nucleotide-count/analysis_options.yaml @@ -0,0 +1,18 @@ +analyzer: + strong-mode: + implicit-casts: false + implicit-dynamic: false + errors: + unused_element: error + unused_import: error + unused_local_variable: error + dead_code: error + +linter: + rules: + # Error Rules + - avoid_relative_lib_imports + - avoid_types_as_parameter_names + - literal_only_boolean_expressions + - no_adjacent_strings_in_list + - valid_regexps diff --git a/exercises/practice/nucleotide-count/lib/nucleotide_count.dart b/exercises/practice/nucleotide-count/lib/nucleotide_count.dart new file mode 100644 index 00000000..3d7a2d6b --- /dev/null +++ b/exercises/practice/nucleotide-count/lib/nucleotide_count.dart @@ -0,0 +1,5 @@ +// Implement a custom InvalidNucleotideException exception + +class NucleotideCount { + // Put your code here +} diff --git a/exercises/practice/nucleotide-count/pubspec.yaml b/exercises/practice/nucleotide-count/pubspec.yaml new file mode 100644 index 00000000..14c5cadc --- /dev/null +++ b/exercises/practice/nucleotide-count/pubspec.yaml @@ -0,0 +1,5 @@ +name: 'nucleotide_count' +environment: + sdk: '>=3.2.0 <4.0.0' +dev_dependencies: + test: '<2.0.0' diff --git a/exercises/practice/nucleotide-count/test/nucleotide_count_test.dart b/exercises/practice/nucleotide-count/test/nucleotide_count_test.dart new file mode 100644 index 00000000..e1323fe6 --- /dev/null +++ b/exercises/practice/nucleotide-count/test/nucleotide_count_test.dart @@ -0,0 +1,41 @@ +import 'package:nucleotide_count/nucleotide_count.dart'; +import 'package:test/test.dart'; + +void main() { + final counter = NucleotideCount(); + + group('NucleotideCount', () { + test('Empty strand', () { + final strand = ""; + final result = counter.count(strand); + final expected = {"A": 0, "C": 0, "G": 0, "T": 0}; + expect(result, equals(expected)); + }, skip: false); + + test('Can count one nucleotide in single-character input', () { + final strand = "G"; + final result = counter.count(strand); + final expected = {"A": 0, "C": 0, "G": 1, "T": 0}; + expect(result, equals(expected)); + }, skip: true); + + test('Strand with repeated nucleotide', () { + final strand = "GGGGGGG"; + final result = counter.count(strand); + final expected = {"A": 0, "C": 0, "G": 7, "T": 0}; + expect(result, equals(expected)); + }, skip: true); + + test('Strand with multiple nucleotides', () { + final strand = "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC"; + final result = counter.count(strand); + final expected = {"A": 20, "C": 12, "G": 17, "T": 21}; + expect(result, equals(expected)); + }, skip: true); + + test('Handles invalid nucleotides', () { + final strand = "AGXXACT"; + expect(() => counter.count(strand), throwsA(const TypeMatcher())); + }, skip: true); + }); +}