primeiro commit com exercicios de Exercism realizados até esta data
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"authors": [],
|
||||
"contributors": [
|
||||
"behrtam",
|
||||
"cmccandless",
|
||||
"crazymykl",
|
||||
"Dog",
|
||||
"dotrungkien",
|
||||
"henrik",
|
||||
"ikhadykin",
|
||||
"kytrinyx",
|
||||
"lowks",
|
||||
"markijbema",
|
||||
"N-Parsons",
|
||||
"n1k0",
|
||||
"pheanex",
|
||||
"roadfoodr",
|
||||
"sjakobi",
|
||||
"thomasjpfan",
|
||||
"tqa236",
|
||||
"vgerak",
|
||||
"yawpitch"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"anagram.py"
|
||||
],
|
||||
"test": [
|
||||
"anagram_test.py"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.py"
|
||||
]
|
||||
},
|
||||
"blurb": "Given a word and a list of possible anagrams, select the correct sublist.",
|
||||
"source": "Inspired by the Extreme Startup game",
|
||||
"source_url": "https://github.com/rchatley/extreme_startup"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"anagram","id":"7031d739171c414490bfc8ee0955d6e0","url":"https://exercism.org/tracks/python/exercises/anagram","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit anagram.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Anagram
|
||||
|
||||
Welcome to Anagram on Exercism's Python Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Introduction
|
||||
|
||||
At a garage sale, you find a lovely vintage typewriter at a bargain price!
|
||||
Excitedly, you rush home, insert a sheet of paper, and start typing away.
|
||||
However, your excitement wanes when you examine the output: all words are garbled!
|
||||
For example, it prints "stop" instead of "post" and "least" instead of "stale."
|
||||
Carefully, you try again, but now it prints "spot" and "slate."
|
||||
After some experimentation, you find there is a random delay before each letter is printed, which messes up the order.
|
||||
You now understand why they sold it for so little money!
|
||||
|
||||
You realize this quirk allows you to generate anagrams, which are words formed by rearranging the letters of another word.
|
||||
Pleased with your finding, you spend the rest of the day generating hundreds of anagrams.
|
||||
|
||||
## Instructions
|
||||
|
||||
Given a target word and one or more candidate words, your task is to find the candidates that are anagrams of the target.
|
||||
|
||||
An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`.
|
||||
A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`.
|
||||
|
||||
The target word and candidate words are made up of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`).
|
||||
Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `"StoP"` is not an anagram of `"sTOp"`.
|
||||
The words you need to find should be taken from the candidate words, using the same letter case.
|
||||
|
||||
Given the target `"stone"` and the candidate words `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, and `"Seton"`, the anagram words you need to find are `"tones"`, `"notes"`, and `"Seton"`.
|
||||
|
||||
## Source
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @behrtam
|
||||
- @cmccandless
|
||||
- @crazymykl
|
||||
- @Dog
|
||||
- @dotrungkien
|
||||
- @henrik
|
||||
- @ikhadykin
|
||||
- @kytrinyx
|
||||
- @lowks
|
||||
- @markijbema
|
||||
- @N-Parsons
|
||||
- @n1k0
|
||||
- @pheanex
|
||||
- @roadfoodr
|
||||
- @sjakobi
|
||||
- @thomasjpfan
|
||||
- @tqa236
|
||||
- @vgerak
|
||||
- @yawpitch
|
||||
|
||||
### Based on
|
||||
|
||||
Inspired by the Extreme Startup game - https://github.com/rchatley/extreme_startup
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
def find_anagrams(word, candidates):
|
||||
anagrams_list: list[str] = []
|
||||
for candidate in candidates:
|
||||
if word.lower() != candidate.lower() and len(word) == len(candidate):
|
||||
is_anagram_flag: bool = True
|
||||
for letter in word.lower():
|
||||
if word.lower().count(letter) != candidate.lower().count(letter):
|
||||
is_anagram_flag = False
|
||||
break
|
||||
if is_anagram_flag:
|
||||
anagrams_list.append(candidate)
|
||||
return anagrams_list
|
||||
@@ -0,0 +1,105 @@
|
||||
# These tests are auto-generated with test data from:
|
||||
# https://github.com/exercism/problem-specifications/tree/main/exercises/anagram/canonical-data.json
|
||||
# File last updated on 2024-02-28
|
||||
|
||||
import unittest
|
||||
|
||||
from anagram import (
|
||||
find_anagrams,
|
||||
)
|
||||
|
||||
|
||||
class AnagramTest(unittest.TestCase):
|
||||
def test_no_matches(self):
|
||||
candidates = ["hello", "world", "zombies", "pants"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("diaper", candidates), expected)
|
||||
|
||||
def test_detects_two_anagrams(self):
|
||||
candidates = ["lemons", "cherry", "melons"]
|
||||
expected = ["lemons", "melons"]
|
||||
self.assertCountEqual(find_anagrams("solemn", candidates), expected)
|
||||
|
||||
def test_does_not_detect_anagram_subsets(self):
|
||||
candidates = ["dog", "goody"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("good", candidates), expected)
|
||||
|
||||
def test_detects_anagram(self):
|
||||
candidates = ["enlists", "google", "inlets", "banana"]
|
||||
expected = ["inlets"]
|
||||
self.assertCountEqual(find_anagrams("listen", candidates), expected)
|
||||
|
||||
def test_detects_three_anagrams(self):
|
||||
candidates = ["gallery", "ballerina", "regally", "clergy", "largely", "leading"]
|
||||
expected = ["gallery", "regally", "largely"]
|
||||
self.assertCountEqual(find_anagrams("allergy", candidates), expected)
|
||||
|
||||
def test_detects_multiple_anagrams_with_different_case(self):
|
||||
candidates = ["Eons", "ONES"]
|
||||
expected = ["Eons", "ONES"]
|
||||
self.assertCountEqual(find_anagrams("nose", candidates), expected)
|
||||
|
||||
def test_does_not_detect_non_anagrams_with_identical_checksum(self):
|
||||
candidates = ["last"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("mass", candidates), expected)
|
||||
|
||||
def test_detects_anagrams_case_insensitively(self):
|
||||
candidates = ["cashregister", "Carthorse", "radishes"]
|
||||
expected = ["Carthorse"]
|
||||
self.assertCountEqual(find_anagrams("Orchestra", candidates), expected)
|
||||
|
||||
def test_detects_anagrams_using_case_insensitive_subject(self):
|
||||
candidates = ["cashregister", "carthorse", "radishes"]
|
||||
expected = ["carthorse"]
|
||||
self.assertCountEqual(find_anagrams("Orchestra", candidates), expected)
|
||||
|
||||
def test_detects_anagrams_using_case_insensitive_possible_matches(self):
|
||||
candidates = ["cashregister", "Carthorse", "radishes"]
|
||||
expected = ["Carthorse"]
|
||||
self.assertCountEqual(find_anagrams("orchestra", candidates), expected)
|
||||
|
||||
def test_does_not_detect_an_anagram_if_the_original_word_is_repeated(self):
|
||||
candidates = ["goGoGO"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("go", candidates), expected)
|
||||
|
||||
def test_anagrams_must_use_all_letters_exactly_once(self):
|
||||
candidates = ["patter"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("tapper", candidates), expected)
|
||||
|
||||
def test_words_are_not_anagrams_of_themselves(self):
|
||||
candidates = ["BANANA"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("BANANA", candidates), expected)
|
||||
|
||||
def test_words_are_not_anagrams_of_themselves_even_if_letter_case_is_partially_different(
|
||||
self,
|
||||
):
|
||||
candidates = ["Banana"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("BANANA", candidates), expected)
|
||||
|
||||
def test_words_are_not_anagrams_of_themselves_even_if_letter_case_is_completely_different(
|
||||
self,
|
||||
):
|
||||
candidates = ["banana"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("BANANA", candidates), expected)
|
||||
|
||||
def test_words_other_than_themselves_can_be_anagrams(self):
|
||||
candidates = ["LISTEN", "Silent"]
|
||||
expected = ["Silent"]
|
||||
self.assertCountEqual(find_anagrams("LISTEN", candidates), expected)
|
||||
|
||||
def test_handles_case_of_greek_letters(self):
|
||||
candidates = ["ΒΓΑ", "ΒΓΔ", "γβα", "αβγ"]
|
||||
expected = ["ΒΓΑ", "γβα"]
|
||||
self.assertCountEqual(find_anagrams("ΑΒΓ", candidates), expected)
|
||||
|
||||
def test_different_characters_may_have_the_same_bytes(self):
|
||||
candidates = ["€a"]
|
||||
expected = []
|
||||
self.assertCountEqual(find_anagrams("a⬂", candidates), expected)
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"authors": [
|
||||
"pheanex"
|
||||
],
|
||||
"contributors": [
|
||||
"BethanyG",
|
||||
"cmccandless",
|
||||
"Dog",
|
||||
"K4cePhoenix",
|
||||
"sukovanej",
|
||||
"tqa236"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"armstrong_numbers.py"
|
||||
],
|
||||
"test": [
|
||||
"armstrong_numbers_test.py"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.py"
|
||||
]
|
||||
},
|
||||
"blurb": "Determine if a number is an Armstrong number.",
|
||||
"source": "Wikipedia",
|
||||
"source_url": "https://en.wikipedia.org/wiki/Narcissistic_number"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"armstrong-numbers","id":"638a18575d6e4c5cb072d8f6d5dfe702","url":"https://exercism.org/tracks/python/exercises/armstrong-numbers","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit armstrong_numbers.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Armstrong Numbers
|
||||
|
||||
Welcome to Armstrong Numbers on Exercism's Python Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
An [Armstrong number][armstrong-number] is a number that is the sum of its own digits each raised to the power of the number of digits.
|
||||
|
||||
For example:
|
||||
|
||||
- 9 is an Armstrong number, because `9 = 9^1 = 9`
|
||||
- 10 is _not_ an Armstrong number, because `10 != 1^2 + 0^2 = 1`
|
||||
- 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153`
|
||||
- 154 is _not_ an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190`
|
||||
|
||||
Write some code to determine whether a number is an Armstrong number.
|
||||
|
||||
[armstrong-number]: https://en.wikipedia.org/wiki/Narcissistic_number
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @pheanex
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @BethanyG
|
||||
- @cmccandless
|
||||
- @Dog
|
||||
- @K4cePhoenix
|
||||
- @sukovanej
|
||||
- @tqa236
|
||||
|
||||
### Based on
|
||||
|
||||
Wikipedia - https://en.wikipedia.org/wiki/Narcissistic_number
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
def is_armstrong_number(number):
|
||||
literal: str = str(number)
|
||||
nr_digits: int = len(literal)
|
||||
sum_of_digits: int = 0
|
||||
for pos in range(nr_digits):
|
||||
sum_of_digits += int(literal[pos]) ** nr_digits
|
||||
return sum_of_digits == number
|
||||
@@ -0,0 +1,38 @@
|
||||
# These tests are auto-generated with test data from:
|
||||
# https://github.com/exercism/problem-specifications/tree/main/exercises/armstrong-numbers/canonical-data.json
|
||||
# File last updated on 2023-07-20
|
||||
|
||||
import unittest
|
||||
|
||||
from armstrong_numbers import (
|
||||
is_armstrong_number,
|
||||
)
|
||||
|
||||
|
||||
class ArmstrongNumbersTest(unittest.TestCase):
|
||||
def test_zero_is_an_armstrong_number(self):
|
||||
self.assertIs(is_armstrong_number(0), True)
|
||||
|
||||
def test_single_digit_numbers_are_armstrong_numbers(self):
|
||||
self.assertIs(is_armstrong_number(5), True)
|
||||
|
||||
def test_there_are_no_two_digit_armstrong_numbers(self):
|
||||
self.assertIs(is_armstrong_number(10), False)
|
||||
|
||||
def test_three_digit_number_that_is_an_armstrong_number(self):
|
||||
self.assertIs(is_armstrong_number(153), True)
|
||||
|
||||
def test_three_digit_number_that_is_not_an_armstrong_number(self):
|
||||
self.assertIs(is_armstrong_number(100), False)
|
||||
|
||||
def test_four_digit_number_that_is_an_armstrong_number(self):
|
||||
self.assertIs(is_armstrong_number(9474), True)
|
||||
|
||||
def test_four_digit_number_that_is_not_an_armstrong_number(self):
|
||||
self.assertIs(is_armstrong_number(9475), False)
|
||||
|
||||
def test_seven_digit_number_that_is_an_armstrong_number(self):
|
||||
self.assertIs(is_armstrong_number(9926315), True)
|
||||
|
||||
def test_seven_digit_number_that_is_not_an_armstrong_number(self):
|
||||
self.assertIs(is_armstrong_number(9926314), False)
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"authors": [
|
||||
"betegelse"
|
||||
],
|
||||
"contributors": [
|
||||
"behrtam",
|
||||
"cmccandless",
|
||||
"Dog",
|
||||
"ikhadykin",
|
||||
"jgomo3",
|
||||
"kytrinyx",
|
||||
"N-Parsons",
|
||||
"parinporecha",
|
||||
"pheanex",
|
||||
"sjakobi",
|
||||
"thomasjpfan",
|
||||
"tqa236"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"atbash_cipher.py"
|
||||
],
|
||||
"test": [
|
||||
"atbash_cipher_test.py"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.py"
|
||||
]
|
||||
},
|
||||
"blurb": "Create an implementation of the Atbash cipher, an ancient encryption system created in the Middle East.",
|
||||
"source": "Wikipedia",
|
||||
"source_url": "https://en.wikipedia.org/wiki/Atbash"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"atbash-cipher","id":"93a4d551fe7d4b36be4720e467aaa8a9","url":"https://exercism.org/tracks/python/exercises/atbash-cipher","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit atbash_cipher.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Atbash Cipher
|
||||
|
||||
Welcome to Atbash Cipher on Exercism's Python Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Create an implementation of the Atbash cipher, an ancient encryption system created in the Middle East.
|
||||
|
||||
The Atbash cipher is a simple substitution cipher that relies on transposing all the letters in the alphabet such that the resulting alphabet is backwards.
|
||||
The first letter is replaced with the last letter, the second with the second-last, and so on.
|
||||
|
||||
An Atbash cipher for the Latin alphabet would be as follows:
|
||||
|
||||
```text
|
||||
Plain: abcdefghijklmnopqrstuvwxyz
|
||||
Cipher: zyxwvutsrqponmlkjihgfedcba
|
||||
```
|
||||
|
||||
It is a very weak cipher because it only has one possible key, and it is a simple mono-alphabetic substitution cipher.
|
||||
However, this may not have been an issue in the cipher's time.
|
||||
|
||||
Ciphertext is written out in groups of fixed length, the traditional group size being 5 letters, leaving numbers unchanged, and punctuation is excluded.
|
||||
This is to make it harder to guess things based on word boundaries.
|
||||
All text will be encoded as lowercase letters.
|
||||
|
||||
## Examples
|
||||
|
||||
- Encoding `test` gives `gvhg`
|
||||
- Encoding `x123 yes` gives `c123b vh`
|
||||
- Decoding `gvhg` gives `test`
|
||||
- Decoding `gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt` gives `thequickbrownfoxjumpsoverthelazydog`
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @betegelse
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @behrtam
|
||||
- @cmccandless
|
||||
- @Dog
|
||||
- @ikhadykin
|
||||
- @jgomo3
|
||||
- @kytrinyx
|
||||
- @N-Parsons
|
||||
- @parinporecha
|
||||
- @pheanex
|
||||
- @sjakobi
|
||||
- @thomasjpfan
|
||||
- @tqa236
|
||||
|
||||
### Based on
|
||||
|
||||
Wikipedia - https://en.wikipedia.org/wiki/Atbash
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
def encode(plain_text: str) -> str:
|
||||
encoded_text: str = ""
|
||||
weight: int = 0
|
||||
for char in plain_text.lower():
|
||||
if char.isalpha():
|
||||
encoded_text += chr(97 + 25 - (ord(char) - 97))
|
||||
weight += 1
|
||||
elif char.isdigit():
|
||||
encoded_text += char
|
||||
weight += 1
|
||||
else:
|
||||
continue
|
||||
if (weight % 5) == 0:
|
||||
encoded_text += " "
|
||||
# remove space at the end when it exists
|
||||
if encoded_text[-1] == " ":
|
||||
return encoded_text[0:-1]
|
||||
return encoded_text
|
||||
|
||||
|
||||
def decode(ciphered_text: str) -> str:
|
||||
decoded_text: str = ""
|
||||
for char in ciphered_text.lower():
|
||||
if char.isalpha():
|
||||
decoded_text += chr(122 - 25 + (122 - ord(char)))
|
||||
elif char.isdigit():
|
||||
decoded_text += char
|
||||
else:
|
||||
continue
|
||||
return decoded_text
|
||||
@@ -0,0 +1,65 @@
|
||||
# These tests are auto-generated with test data from:
|
||||
# https://github.com/exercism/problem-specifications/tree/main/exercises/atbash-cipher/canonical-data.json
|
||||
# File last updated on 2023-07-20
|
||||
|
||||
import unittest
|
||||
|
||||
from atbash_cipher import (
|
||||
decode,
|
||||
encode,
|
||||
)
|
||||
|
||||
|
||||
class AtbashCipherTest(unittest.TestCase):
|
||||
def test_encode_yes(self):
|
||||
self.assertEqual(encode("yes"), "bvh")
|
||||
|
||||
def test_encode_no(self):
|
||||
self.assertEqual(encode("no"), "ml")
|
||||
|
||||
def test_encode_omg(self):
|
||||
self.assertEqual(encode("OMG"), "lnt")
|
||||
|
||||
def test_encode_spaces(self):
|
||||
self.assertEqual(encode("O M G"), "lnt")
|
||||
|
||||
def test_encode_mindblowingly(self):
|
||||
self.assertEqual(encode("mindblowingly"), "nrmwy oldrm tob")
|
||||
|
||||
def test_encode_numbers(self):
|
||||
self.assertEqual(encode("Testing,1 2 3, testing."), "gvhgr mt123 gvhgr mt")
|
||||
|
||||
def test_encode_deep_thought(self):
|
||||
self.assertEqual(encode("Truth is fiction."), "gifgs rhurx grlm")
|
||||
|
||||
def test_encode_all_the_letters(self):
|
||||
self.assertEqual(
|
||||
encode("The quick brown fox jumps over the lazy dog."),
|
||||
"gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt",
|
||||
)
|
||||
|
||||
def test_decode_exercism(self):
|
||||
self.assertEqual(decode("vcvix rhn"), "exercism")
|
||||
|
||||
def test_decode_a_sentence(self):
|
||||
self.assertEqual(
|
||||
decode("zmlyh gzxov rhlug vmzhg vkkrm thglm v"),
|
||||
"anobstacleisoftenasteppingstone",
|
||||
)
|
||||
|
||||
def test_decode_numbers(self):
|
||||
self.assertEqual(decode("gvhgr mt123 gvhgr mt"), "testing123testing")
|
||||
|
||||
def test_decode_all_the_letters(self):
|
||||
self.assertEqual(
|
||||
decode("gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt"),
|
||||
"thequickbrownfoxjumpsoverthelazydog",
|
||||
)
|
||||
|
||||
def test_decode_with_too_many_spaces(self):
|
||||
self.assertEqual(decode("vc vix r hn"), "exercism")
|
||||
|
||||
def test_decode_with_no_spaces(self):
|
||||
self.assertEqual(
|
||||
decode("zmlyhgzxovrhlugvmzhgvkkrmthglmv"), "anobstacleisoftenasteppingstone"
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"authors": [
|
||||
"michaelkunc"
|
||||
],
|
||||
"contributors": [
|
||||
"AnAccountForReportingBugs",
|
||||
"behrtam",
|
||||
"BethanyG",
|
||||
"cmccandless",
|
||||
"Dog",
|
||||
"fortrieb",
|
||||
"ikhadykin",
|
||||
"kytrinyx",
|
||||
"mpatibandla",
|
||||
"N-Parsons",
|
||||
"patricksjackson",
|
||||
"pheanex",
|
||||
"subkrish",
|
||||
"tqa236"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"binary_search.py"
|
||||
],
|
||||
"test": [
|
||||
"binary_search_test.py"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.py"
|
||||
]
|
||||
},
|
||||
"blurb": "Implement a binary search algorithm.",
|
||||
"source": "Wikipedia",
|
||||
"source_url": "https://en.wikipedia.org/wiki/Binary_search_algorithm"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"binary-search","id":"b5256f28c8a741168d520136d8106a26","url":"https://exercism.org/tracks/python/exercises/binary-search","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit binary_search.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Binary Search
|
||||
|
||||
Welcome to Binary Search on Exercism's Python Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Introduction
|
||||
|
||||
You have stumbled upon a group of mathematicians who are also singer-songwriters.
|
||||
They have written a song for each of their favorite numbers, and, as you can imagine, they have a lot of favorite numbers (like [0][zero] or [73][seventy-three] or [6174][kaprekars-constant]).
|
||||
|
||||
You are curious to hear the song for your favorite number, but with so many songs to wade through, finding the right song could take a while.
|
||||
Fortunately, they have organized their songs in a playlist sorted by the title — which is simply the number that the song is about.
|
||||
|
||||
You realize that you can use a binary search algorithm to quickly find a song given the title.
|
||||
|
||||
[zero]: https://en.wikipedia.org/wiki/0
|
||||
[seventy-three]: https://en.wikipedia.org/wiki/73_(number)
|
||||
[kaprekars-constant]: https://en.wikipedia.org/wiki/6174_(number)
|
||||
|
||||
## Instructions
|
||||
|
||||
Your task is to implement a binary search algorithm.
|
||||
|
||||
A binary search algorithm finds an item in a list by repeatedly splitting it in half, only keeping the half which contains the item we're looking for.
|
||||
It allows us to quickly narrow down the possible locations of our item until we find it, or until we've eliminated all possible locations.
|
||||
|
||||
~~~~exercism/caution
|
||||
Binary search only works when a list has been sorted.
|
||||
~~~~
|
||||
|
||||
The algorithm looks like this:
|
||||
|
||||
- Find the middle element of a _sorted_ list and compare it with the item we're looking for.
|
||||
- If the middle element is our item, then we're done!
|
||||
- If the middle element is greater than our item, we can eliminate that element and all the elements **after** it.
|
||||
- If the middle element is less than our item, we can eliminate that element and all the elements **before** it.
|
||||
- If every element of the list has been eliminated then the item is not in the list.
|
||||
- Otherwise, repeat the process on the part of the list that has not been eliminated.
|
||||
|
||||
Here's an example:
|
||||
|
||||
Let's say we're looking for the number 23 in the following sorted list: `[4, 8, 12, 16, 23, 28, 32]`.
|
||||
|
||||
- We start by comparing 23 with the middle element, 16.
|
||||
- Since 23 is greater than 16, we can eliminate the left half of the list, leaving us with `[23, 28, 32]`.
|
||||
- We then compare 23 with the new middle element, 28.
|
||||
- Since 23 is less than 28, we can eliminate the right half of the list: `[23]`.
|
||||
- We've found our item.
|
||||
|
||||
## Exception messages
|
||||
|
||||
Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.
|
||||
|
||||
This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" a `ValueError` when the given value is not found within the array. The tests will only pass if you both `raise` the `exception` and include a message with it.
|
||||
|
||||
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
|
||||
|
||||
```python
|
||||
# example when value is not found in the array.
|
||||
raise ValueError("value not in array")
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @michaelkunc
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @AnAccountForReportingBugs
|
||||
- @behrtam
|
||||
- @BethanyG
|
||||
- @cmccandless
|
||||
- @Dog
|
||||
- @fortrieb
|
||||
- @ikhadykin
|
||||
- @kytrinyx
|
||||
- @mpatibandla
|
||||
- @N-Parsons
|
||||
- @patricksjackson
|
||||
- @pheanex
|
||||
- @subkrish
|
||||
- @tqa236
|
||||
|
||||
### Based on
|
||||
|
||||
Wikipedia - https://en.wikipedia.org/wiki/Binary_search_algorithm
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
# iterative version of the code
|
||||
# def find(search_list: list[int], value: int) -> int:
|
||||
# left: int = 0
|
||||
# right: int = len(search_list) - 1
|
||||
# search_list.sort()
|
||||
# while left <= right:
|
||||
# middle_index: int = (left + right) // 2
|
||||
# if value < search_list[middle_index]:
|
||||
# right = middle_index - 1
|
||||
# elif value > search_list[middle_index]:
|
||||
# left = middle_index + 1
|
||||
# else:
|
||||
# return middle_index
|
||||
# raise ValueError(f"value not in array")
|
||||
|
||||
|
||||
# recursive version of the code
|
||||
def find(search_list: list[int], value: int) -> int:
|
||||
middle_index: int = (len(search_list) - 1) // 2
|
||||
search_list.sort()
|
||||
if search_list == []:
|
||||
raise ValueError("value not in array")
|
||||
if value < search_list[middle_index]:
|
||||
return find(search_list[:middle_index], value)
|
||||
elif value > search_list[middle_index]:
|
||||
return (middle_index + 1) + find(search_list[middle_index + 1:], value)
|
||||
else:
|
||||
return middle_index
|
||||
@@ -0,0 +1,67 @@
|
||||
# These tests are auto-generated with test data from:
|
||||
# https://github.com/exercism/problem-specifications/tree/main/exercises/binary-search/canonical-data.json
|
||||
# File last updated on 2023-07-20
|
||||
|
||||
import unittest
|
||||
|
||||
from binary_search import (
|
||||
find,
|
||||
)
|
||||
|
||||
|
||||
class BinarySearchTest(unittest.TestCase):
|
||||
def test_finds_a_value_in_an_array_with_one_element(self):
|
||||
self.assertEqual(find([6], 6), 0)
|
||||
|
||||
def test_finds_a_value_in_the_middle_of_an_array(self):
|
||||
self.assertEqual(find([1, 3, 4, 6, 8, 9, 11], 6), 3)
|
||||
|
||||
def test_finds_a_value_at_the_beginning_of_an_array(self):
|
||||
self.assertEqual(find([1, 3, 4, 6, 8, 9, 11], 1), 0)
|
||||
|
||||
def test_finds_a_value_at_the_end_of_an_array(self):
|
||||
self.assertEqual(find([1, 3, 4, 6, 8, 9, 11], 11), 6)
|
||||
|
||||
def test_finds_a_value_in_an_array_of_odd_length(self):
|
||||
self.assertEqual(
|
||||
find([1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634], 144), 9
|
||||
)
|
||||
|
||||
def test_finds_a_value_in_an_array_of_even_length(self):
|
||||
self.assertEqual(
|
||||
find([1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377], 21), 5)
|
||||
|
||||
def test_identifies_that_a_value_is_not_included_in_the_array(self):
|
||||
with self.assertRaises(ValueError) as err:
|
||||
find([1, 3, 4, 6, 8, 9, 11], 7)
|
||||
|
||||
self.assertEqual(type(err.exception), ValueError)
|
||||
self.assertEqual(err.exception.args[0], "value not in array")
|
||||
|
||||
def test_a_value_smaller_than_the_array_s_smallest_value_is_not_found(self):
|
||||
with self.assertRaises(ValueError) as err:
|
||||
find([1, 3, 4, 6, 8, 9, 11], 0)
|
||||
|
||||
self.assertEqual(type(err.exception), ValueError)
|
||||
self.assertEqual(err.exception.args[0], "value not in array")
|
||||
|
||||
def test_a_value_larger_than_the_array_s_largest_value_is_not_found(self):
|
||||
with self.assertRaises(ValueError) as err:
|
||||
find([1, 3, 4, 6, 8, 9, 11], 13)
|
||||
|
||||
self.assertEqual(type(err.exception), ValueError)
|
||||
self.assertEqual(err.exception.args[0], "value not in array")
|
||||
|
||||
def test_nothing_is_found_in_an_empty_array(self):
|
||||
with self.assertRaises(ValueError) as err:
|
||||
find([], 1)
|
||||
|
||||
self.assertEqual(type(err.exception), ValueError)
|
||||
self.assertEqual(err.exception.args[0], "value not in array")
|
||||
|
||||
def test_nothing_is_found_when_the_left_and_right_bounds_cross(self):
|
||||
with self.assertRaises(ValueError) as err:
|
||||
find([1, 2], 0)
|
||||
|
||||
self.assertEqual(type(err.exception), ValueError)
|
||||
self.assertEqual(err.exception.args[0], "value not in array")
|
||||
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"authors": [
|
||||
"Ticktakto",
|
||||
"Yabby1997",
|
||||
"limm-jk",
|
||||
"OMEGA-Y",
|
||||
"wnstj2007",
|
||||
"pranasziaukas",
|
||||
"bethanyG"
|
||||
],
|
||||
"contributors": [
|
||||
"PaulT89"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"black_jack.py"
|
||||
],
|
||||
"test": [
|
||||
"black_jack_test.py"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.py"
|
||||
]
|
||||
},
|
||||
"icon": "poker",
|
||||
"blurb": "Learn about comparisons by implementing some Black Jack judging rules."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"black-jack","id":"9e1cc775a71046d1a495346a5c712920","url":"https://exercism.org/tracks/python/exercises/black-jack","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit black_jack.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Hints
|
||||
|
||||
[The Python comparisons tutorial][python comparisons tutorial] and [Python comparisons examples][python comparisons examples] are a great introduction covering the content of this exercise.
|
||||
|
||||
## 1. Calculate the value of a card
|
||||
|
||||
- You can use the equality comparison operator `==` to determine if a card is an ace card: `card == 'A'`.
|
||||
- You can use the containment operator `in` to determine if a substring is contained inside a string: `'Q' in 'KJQ'`.
|
||||
- You can use the [`int` constructor][int constructor] to convert a `str` of an `int` to an `int`: `int('13')`.
|
||||
|
||||
## 2. Determine which card has a higher value
|
||||
|
||||
- Once you have defined the `value_of_card` function, you can call it from other functions.
|
||||
- You can use the value comparison operators `>` and `<` to determine if specific cards are _greater than_ or _less than_ a given value: `3 < 12`.
|
||||
- You can use the equality comparison operator `==` to determine if two values are equal to one another.
|
||||
|
||||
## 3. Calculate the value of an ace
|
||||
|
||||
- Once you have defined the `value_of_card` function, you can call it from other functions.
|
||||
- You can use the order comparison operator `>` to decide the appropriate course of action here.
|
||||
|
||||
## 4. Determine Blackjack
|
||||
|
||||
- Remember, you can use the [`if`/`elif`/`else` syntax][if syntax] to handle different combinations of cards.
|
||||
- You can chain BOTH comparison operators and boolean operators _arbitrarily_: `y < z < x` or `(y or z) and (x or z)`
|
||||
- You can reuse the already implemented `value_of_card` function.
|
||||
|
||||
## 5. Splitting pairs
|
||||
|
||||
- You can reuse the already implemented `value_of_card` function.
|
||||
- You can handle the `A` case (when at least one of the cards in an ace) separately.
|
||||
|
||||
## 6. Doubling down
|
||||
|
||||
- An `A` scored at 11 will never allow doubling down if there are two cards in the hand.
|
||||
- Given the first point, you _should_ be able to reuse the already implemented `value_of_card` function.
|
||||
- You can chain comparison operators _arbitrarily_: `y < z < x`.
|
||||
- You can use the [conditional expression][conditional expression] (_sometimes called a "ternary operator"_)
|
||||
to shorten simple `if`/`else` statements: `13 if letter == 'M' else 3`.
|
||||
|
||||
[conditional expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions
|
||||
[if syntax]: https://docs.python.org/3/tutorial/controlflow.html#if-statements
|
||||
[int constructor]: https://docs.python.org/3/library/functions.html#int
|
||||
[python comparisons examples]: https://www.tutorialspoint.com/python/comparison_operators_example.htm
|
||||
[python comparisons tutorial]: https://docs.python.org/3/reference/expressions.html#comparisons
|
||||
@@ -0,0 +1,395 @@
|
||||
# Black Jack
|
||||
|
||||
Welcome to Black Jack on Exercism's Python 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
|
||||
|
||||
## Comparisons
|
||||
|
||||
Python supports the following basic comparison operators:
|
||||
|
||||
| Operator | Operation | Description |
|
||||
| -------- | -------------------------- | ------------------------------------------------------------------------- |
|
||||
| `>` | "greater than" | `a > b` is `True` if `a` is **strictly** greater in value than `b` |
|
||||
| `<` | "less than" | `a < b` is `True` if `a` is **strictly** less in value than `b` |
|
||||
| `==` | "equal to" | `a == b` is `True` if `a` is **strictly** equal to `b` in value |
|
||||
| `>=` | "greater than or equal to" | `a >= b` is `True` if `a > b` OR `a == b` in value |
|
||||
| `<=` | "less than or equal to" | `a <= b` is `True` if `a < b` or `a == b` in value |
|
||||
| `!=` | "not equal to" | `a != b` is `True` if `a == b` is `False` |
|
||||
| `is` | "identity" | `a is b` is `True` if **_and only if_** `a` and `b` are the same _object_ |
|
||||
| `is not` | "negated identity" | `a is not b` is `True` if `a` and `b` are **not** the same _object_ |
|
||||
| `in` | "containment test" | `a in b` is `True` if `a` is member, subset, or element of `b` |
|
||||
| `not in` | "negated containment test" | `a not in b` is `True` if `a` is not a member, subset, or element of `b` |
|
||||
|
||||
They all have the same priority (_which is higher than that of [Boolean operations][boolean operations], but lower than that of arithmetic or bitwise operations_).
|
||||
|
||||
## Comparison between different data types
|
||||
|
||||
Objects that are different types (_except numeric types_) never compare equal by default.
|
||||
Non-identical instances of a `class` will also _**not**_ compare as equal unless the `class` defines special [rich comparison][rich comparisons] methods that customize the default `object` comparison behavior.
|
||||
Customizing via `rich comparisons` will be covered in a follow-on exercise.
|
||||
For (much) more detail on this topic, see [Value comparisons][value comparisons] in the Python documentation.
|
||||
|
||||
Numeric types are (mostly) an exception to this type matching rule.
|
||||
An `integer` **can** be considered equal to a `float` (_or an [`octal`][octal] equal to a [`hexadecimal`][hex]_), as long as the types can be implicitly converted for comparison.
|
||||
|
||||
For the other numeric types in the Python standard library ([complex][complex numbers], [decimal][decimal numbers], [fractions][rational numbers]), comparison operators are defined where they "make sense" (_where implicit conversion does not change the outcome_), but throw a `TypeError` if the underlying objects cannot be accurately converted for comparison.
|
||||
For more information on the rules that python uses for _numeric conversion_, see [arithmetic conversions][arithmetic conversions] in the Python documentation.
|
||||
|
||||
```python
|
||||
>>> import fractions
|
||||
|
||||
# A string cannot be converted to an int.
|
||||
>>> 17 == '17'
|
||||
False
|
||||
|
||||
# An int can be converted to float for comparison.
|
||||
>>> 17 == 17.0
|
||||
True
|
||||
|
||||
# The fraction 6/3 can be converted to the int 2
|
||||
# The int 2 can be converted to 0b10 in binary.
|
||||
>>> 6/3 == 0b10
|
||||
True
|
||||
|
||||
# An int can be converted to a complex number with a 0 imaginary part.
|
||||
>>> 17 == complex(17)
|
||||
True
|
||||
|
||||
# The fraction 2/5 can be converted to the float 0.4
|
||||
>>> 0.4 == 2/5
|
||||
True
|
||||
|
||||
>>> complex(2/5, 1/2) == complex(0.4, 0.5)
|
||||
True
|
||||
```
|
||||
|
||||
Any ordered comparison of a number to a `NaN` (_not a number_) type is `False`.
|
||||
A confusing side effect of Python's `NaN` definition is that `NaN` never compares equal to `NaN`.
|
||||
|
||||
```python
|
||||
>>> x = float('NaN')
|
||||
|
||||
>>> 3 < x
|
||||
False
|
||||
|
||||
>>> x < 3
|
||||
False
|
||||
|
||||
# NaN never compares equal to NaN
|
||||
>>> x == x
|
||||
False
|
||||
```
|
||||
|
||||
## Comparing Strings
|
||||
|
||||
Unlike numbers, strings (`str`) are compared [_lexicographically_][lexographic order], using their individual Unicode code points (_the result of passing each code point in the `str` to the built-in function [`ord()`][ord], which returns an `int`_).
|
||||
If all code points in both strings match and are _**in the same order**_, the two strings are considered equal.
|
||||
This comparison is done in a 'pair-wise' fashion - first-to-first, second-to-second, etc.
|
||||
In Python 3.x, `str` and `bytes` cannot be directly coerced/compared.
|
||||
|
||||
```python
|
||||
>>> 'Python' > 'Rust'
|
||||
False
|
||||
|
||||
>>> 'Python' > 'JavaScript'
|
||||
True
|
||||
|
||||
# Examples with Mandarin.
|
||||
# hello < goodbye
|
||||
>>> '你好' < '再见'
|
||||
True
|
||||
|
||||
# ord() of first characters
|
||||
>>> ord('你'), ord('再')
|
||||
(20320, 20877)
|
||||
|
||||
# ord() of second characters
|
||||
>>> ord('好'), ord('见')
|
||||
(22909, 35265)
|
||||
|
||||
# And with Korean words.
|
||||
# Pretty < beautiful.
|
||||
>>> '예쁜' < '아름다운'
|
||||
False
|
||||
|
||||
>>> ord('예'), ord('아')
|
||||
(50696, 50500)
|
||||
```
|
||||
|
||||
## Comparison Chaining
|
||||
|
||||
Comparison operators can be chained _arbitrarily_ -- meaning that they can be used in any combination of any length.
|
||||
Note that the evaluation of an expression takes place from `left` to `right`.
|
||||
|
||||
As an example, `x < y <= z` is equivalent to `x < y` `and` `y <= z`, except that `y` is evaluated **only once**.
|
||||
In both cases, `z` is _not_ evaluated **at all** when `x < y` is found to be `False`.
|
||||
This is often called `short-circuit evaluation` - the evaluation stops if the truth value of the expression has already been determined.
|
||||
|
||||
`Short circuiting` is supported by various boolean operators, functions, and also by comparison chaining in Python.
|
||||
Unlike many other programming languages, including `C`, `C++`, `C#`, and `Java`, chained expressions like `a < b < c` in Python have a conventional [mathematical interpretation][three way boolean comparison] and precedence.
|
||||
|
||||
```python
|
||||
>>> x = 2
|
||||
>>> y = 5
|
||||
>>> z = 10
|
||||
|
||||
>>> x < y < z
|
||||
True
|
||||
|
||||
>>> x < y > z
|
||||
False
|
||||
|
||||
>>> x > y < z
|
||||
False
|
||||
```
|
||||
|
||||
## Comparing object identity
|
||||
|
||||
The operators `is` and `is not` test for object [_identity_][object identity], as opposed to object _value_.
|
||||
An object's identity never changes after creation and can be found by using the [`id()`][id function] function.
|
||||
|
||||
`<apple> is <orange>` evaluates to `True` if _**and only if**_ `id(<apple>)` == `id(<orange>)`.
|
||||
`<apple> is not <orange>` yields the inverse.
|
||||
|
||||
Due to their singleton status, `None` and `NotImplemented` should always be compared to items using `is` and `is not`.
|
||||
See the Python reference docs on [value comparisons][value comparisons none] and [PEP8][pep8 programming recommendations] for more details on this convention.
|
||||
|
||||
```python
|
||||
>>> my_fav_numbers = [1, 2, 3]
|
||||
|
||||
>>> your_fav_numbers = my_fav_numbers
|
||||
|
||||
>>> my_fav_numbers is your_fav_numbers
|
||||
True
|
||||
|
||||
# The returned id will differ by system and python version.
|
||||
>>> id(my_fav_numbers)
|
||||
4517478208
|
||||
|
||||
# your_fav_numbers is only an alias pointing to the original my_fav_numbers object.
|
||||
# Assigning a new name does not create a new object.
|
||||
>>> id(your_fav_numbers)
|
||||
4517478208
|
||||
|
||||
|
||||
>>> my_fav_numbers is not your_fav_numbers
|
||||
False
|
||||
|
||||
>>> my_fav_numbers is not None
|
||||
True
|
||||
|
||||
>>> my_fav_numbers is NotImplemented
|
||||
False
|
||||
```
|
||||
|
||||
## Membership comparisons
|
||||
|
||||
The operators `in` and `not in` test for _membership_.
|
||||
`<fish> in <soup>` evaluates to `True` if `<fish>` is a member of `<soup>` (_if `<fish>` is a subset of or is contained within `<soup>`_), and evaluates `False` otherwise.
|
||||
`<fish> not in <soup>` returns the negation, or _opposite of_ `<fish> in <soup>`.
|
||||
|
||||
For string and bytes types, `<name> in <fullname>` is `True` _**if and only if**_ `<name>` is a substring of `<fullname>`.
|
||||
|
||||
```python
|
||||
# A set of lucky numbers.
|
||||
>>> lucky_numbers = {11, 22, 33}
|
||||
>>> 22 in lucky_numbers
|
||||
True
|
||||
|
||||
>>> 44 in lucky_numbers
|
||||
False
|
||||
|
||||
# A dictionary of employee information.
|
||||
>>> employee = {'name': 'John Doe',
|
||||
'id': 67826, 'age': 33,
|
||||
'title': 'ceo'}
|
||||
|
||||
# Checking for the membership of certain keys.
|
||||
>>> 'age' in employee
|
||||
True
|
||||
|
||||
>>> 33 in employee
|
||||
False
|
||||
|
||||
>>> 'lastname' not in employee
|
||||
True
|
||||
|
||||
# Checking for substring membership
|
||||
>>> name = 'Super Batman'
|
||||
>>> 'Bat' in name
|
||||
True
|
||||
|
||||
>>> 'Batwoman' in name
|
||||
False
|
||||
```
|
||||
|
||||
[arithmetic conversions]: https://docs.python.org/3/reference/expressions.html?highlight=number%20conversion#arithmetic-conversions
|
||||
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
|
||||
[complex numbers]: https://docs.python.org/3/library/functions.html#complex
|
||||
[decimal numbers]: https://docs.python.org/3/library/decimal.html
|
||||
[hex]: https://docs.python.org/3/library/functions.html?highlight=hex#hex
|
||||
[id function]: https://docs.python.org/3/library/functions.html#id
|
||||
[lexographic order]: https://en.wikipedia.org/wiki/Lexicographic_order
|
||||
[object identity]: https://docs.python.org/3/reference/datamodel.html
|
||||
[octal]: https://docs.python.org/3/library/functions.html?#oct
|
||||
[ord]: https://docs.python.org/3/library/functions.html#ord
|
||||
[pep8 programming recommendations]: https://pep8.org/#programming-recommendations
|
||||
[rational numbers]: https://docs.python.org/3/library/fractions.html
|
||||
[rich comparisons]: https://docs.python.org/3/reference/datamodel.html#object.__lt__
|
||||
[three way boolean comparison]: https://en.wikipedia.org/wiki/Three-way_comparison
|
||||
[value comparisons none]: https://docs.python.org/3/reference/expressions.html?highlight=none#value-comparisons
|
||||
[value comparisons]: https://docs.python.org/3/reference/expressions.html?highlight=nan#value-comparisons
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise you are going to implement some rules of [Blackjack][blackjack],
|
||||
such as the way the game is played and scored.
|
||||
|
||||
**Note** : In this exercise, _`A`_ means ace, _`J`_ means jack, _`Q`_ means queen, and _`K`_ means king.
|
||||
Jokers are discarded.
|
||||
A [standard French-suited 52-card deck][standard_deck] is assumed, but in most versions, several decks are shuffled together for play.
|
||||
|
||||
## 1. Calculate the value of a card
|
||||
|
||||
In Blackjack, it is up to each individual player if an ace is worth 1 or 11 points (_more on that later_).
|
||||
Face cards (`J`, `Q`, `K`) are scored at 10 points and any other card is worth its "pip" (_numerical_) value.
|
||||
|
||||
Define the `value_of_card(<card>)` function with parameter `card`.
|
||||
The function should return the _numerical value_ of the passed-in card string.
|
||||
Since an ace can take on multiple values (1 **or** 11), this function should fix the value of an ace card at 1 for the time being.
|
||||
Later on, you will implement a function to determine the value of an ace card, given an existing hand.
|
||||
|
||||
```python
|
||||
>>> value_of_card('K')
|
||||
10
|
||||
|
||||
>>> value_of_card('4')
|
||||
4
|
||||
|
||||
>>> value_of_card('A')
|
||||
1
|
||||
```
|
||||
|
||||
## 2. Determine which card has a higher value
|
||||
|
||||
Define the `higher_card(<card_one>, <card_two>)` function having parameters `card_one` and `card_two`.
|
||||
For scoring purposes, the value of `J`, `Q` or `K` is 10.
|
||||
The function should return which card has the higher value for scoring.
|
||||
If both cards have an equal value, return both.
|
||||
Returning both cards can be done by using a comma in the `return` statement:
|
||||
|
||||
```python
|
||||
# Using a comma in a return creates a Tuple. Tuples will be covered in a later exercise.
|
||||
>>> def returning_two_values(value_one, value_two):
|
||||
return value_one, value_two
|
||||
|
||||
>>> returning_two_values('K', '3')
|
||||
('K', '3')
|
||||
```
|
||||
|
||||
An ace can take on multiple values, so we will fix `A` cards to a value of 1 for this task.
|
||||
|
||||
```python
|
||||
>>> higher_card('K', '10')
|
||||
('K', '10')
|
||||
|
||||
>>> higher_card('4', '6')
|
||||
'6'
|
||||
|
||||
>>> higher_card('K', 'A')
|
||||
'K'
|
||||
```
|
||||
|
||||
## 3. Calculate the value of an ace
|
||||
|
||||
As mentioned before, an ace can be worth _either_ 1 **or** 11 points.
|
||||
Players try to get as close as possible to a score of 21, without going _over_ 21 (_going "bust"_).
|
||||
|
||||
Define the `value_of_ace(<card_one>, <card_two>)` function with parameters `card_one` and `card_two`, which are a pair of cards already in the hand _before_ getting an ace card.
|
||||
Your function will have to decide if the upcoming ace will get a value of 1 or a value of 11, and return that value.
|
||||
Remember: the value of the hand with the ace needs to be as high as possible _without_ going over 21.
|
||||
|
||||
**Hint**: if we already have an ace in hand, then the value for the upcoming ace would be 1.
|
||||
|
||||
```python
|
||||
>>> value_of_ace('6', 'K')
|
||||
1
|
||||
|
||||
>>> value_of_ace('7', '3')
|
||||
11
|
||||
```
|
||||
|
||||
## 4. Determine a "Natural" or "Blackjack" Hand
|
||||
|
||||
If a player is dealt an ace (`A`) and a ten-card (10, `K`, `Q`, or `J`) as their first two cards, then the player has a score of 21.
|
||||
This is known as a **blackjack** hand.
|
||||
|
||||
|
||||
Define the `is_blackjack(<card_one>, <card_two>)` function with parameters `card_one` and `card_two`, which are a pair of cards.
|
||||
Determine if the two-card hand is a **blackjack**, and return the boolean `True` if it is, `False` otherwise.
|
||||
|
||||
**Note** : The score _calculation_ can be done in many ways.
|
||||
But if possible, we'd like you to check if there is an ace and a ten-card **_in_** the hand (_or at a certain position_), as opposed to _summing_ the hand values.
|
||||
|
||||
```python
|
||||
>>> is_blackjack('A', 'K')
|
||||
True
|
||||
|
||||
>>> is_blackjack('10', '9')
|
||||
False
|
||||
```
|
||||
|
||||
## 5. Splitting pairs
|
||||
|
||||
If the players first two cards are of the same value, such as two sixes, or a `Q` and `K` a player may choose to treat them as two separate hands.
|
||||
This is known as "splitting pairs".
|
||||
|
||||
Define the `can_split_pairs(<card_one>, <card_two>)` function with parameters `card_one` and `card_two`, which are a pair of cards.
|
||||
Determine if this two-card hand can be split into two pairs.
|
||||
If the hand can be split, return the boolean `True` otherwise, return `False`
|
||||
|
||||
```python
|
||||
>>> can_split_pairs('Q', 'K')
|
||||
True
|
||||
|
||||
>>> can_split_pairs('10', 'A')
|
||||
False
|
||||
```
|
||||
|
||||
## 6. Doubling down
|
||||
|
||||
When the original two cards dealt total 9, 10, or 11 points, a player can place an additional bet equal to their original bet.
|
||||
This is known as "doubling down".
|
||||
|
||||
Define the `can_double_down(<card_one>, <card_two>)` function with parameters `card_one` and `card_two`, which are a pair of cards.
|
||||
Determine if the two-card hand can be "doubled down", and return the boolean `True` if it can, `False` otherwise.
|
||||
|
||||
```python
|
||||
>>> can_double_down('A', '9')
|
||||
True
|
||||
|
||||
>>> can_double_down('10', '2')
|
||||
False
|
||||
```
|
||||
|
||||
[blackjack]: https://bicyclecards.com/how-to-play/blackjack/
|
||||
[standard_deck]: https://en.wikipedia.org/wiki/Standard_52-card_deck
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @Ticktakto
|
||||
- @Yabby1997
|
||||
- @limm-jk
|
||||
- @OMEGA-Y
|
||||
- @wnstj2007
|
||||
- @pranasziaukas
|
||||
- @bethanyG
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @PaulT89
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,103 @@
|
||||
"""Functions to help play and score a game of blackjack.
|
||||
|
||||
How to play blackjack: https://bicyclecards.com/how-to-play/blackjack/
|
||||
"Standard" playing cards: https://en.wikipedia.org/wiki/Standard_52-card_deck
|
||||
"""
|
||||
|
||||
|
||||
def value_of_card(card):
|
||||
"""Determine the scoring value of a card.
|
||||
|
||||
:param card: str - given card.
|
||||
:return: int - value of a given card. See below for values.
|
||||
|
||||
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
|
||||
2. 'A' (ace card) = 1
|
||||
3. '2' - '10' = numerical value.
|
||||
"""
|
||||
match card:
|
||||
case "J" | "Q" | "K":
|
||||
return 10
|
||||
case "A":
|
||||
return 1
|
||||
case _:
|
||||
return int(card)
|
||||
|
||||
|
||||
def higher_card(card_one, card_two):
|
||||
"""Determine which card has a higher value in the hand.
|
||||
|
||||
:param card_one, card_two: str - cards dealt in hand. See below for values.
|
||||
:return: str or tuple - resulting Tuple contains both cards if they are of equal value.
|
||||
|
||||
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
|
||||
2. 'A' (ace card) = 1
|
||||
3. '2' - '10' = numerical value.
|
||||
"""
|
||||
if value_of_card(card_one) > value_of_card(card_two):
|
||||
return card_one
|
||||
if value_of_card(card_one) < value_of_card(card_two):
|
||||
return card_two
|
||||
return (card_one, card_two)
|
||||
|
||||
|
||||
def value_of_ace(card_one, card_two):
|
||||
"""Calculate the most advantageous value for the ace card.
|
||||
|
||||
:param card_one, card_two: str - card dealt. See below for values.
|
||||
:return: int - either 1 or 11 value of the upcoming ace card.
|
||||
|
||||
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
|
||||
2. 'A' (ace card) = 11 (if already in hand)
|
||||
3. '2' - '10' = numerical value.
|
||||
"""
|
||||
card_one_value: int = 11 if card_one == "A" else value_of_card(card_one)
|
||||
card_two_value: int = 11 if card_two == "A" else value_of_card(card_two)
|
||||
if card_one_value + card_two_value <= 10:
|
||||
return 11
|
||||
return 1
|
||||
|
||||
|
||||
def is_blackjack(card_one, card_two):
|
||||
"""Determine if the hand is a 'natural' or 'blackjack'.
|
||||
|
||||
:param card_one, card_two: str - card dealt. See below for values.
|
||||
:return: bool - is the hand is a blackjack (two cards worth 21).
|
||||
|
||||
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
|
||||
2. 'A' (ace card) = 11 (if already in hand)
|
||||
3. '2' - '10' = numerical value.
|
||||
"""
|
||||
card_one_value: int = 11 if card_one == "A" else value_of_card(card_one)
|
||||
card_two_value: int = 0
|
||||
if card_one == "A":
|
||||
card_two_value = value_of_card(card_two)
|
||||
else:
|
||||
card_two_value = 11 if card_two == "A" else value_of_card(card_two)
|
||||
if card_one_value + card_two_value == 21:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def can_split_pairs(card_one, card_two):
|
||||
"""Determine if a player can split their hand into two hands.
|
||||
|
||||
:param card_one, card_two: str - cards dealt.
|
||||
:return: bool - can the hand be split into two pairs? (i.e. cards are of the same value).
|
||||
"""
|
||||
if value_of_card(card_one) == value_of_card(card_two):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def can_double_down(card_one, card_two):
|
||||
"""Determine if a blackjack player can place a double down bet.
|
||||
|
||||
:param card_one, card_two: str - first and second cards in hand.
|
||||
:return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points).
|
||||
"""
|
||||
match value_of_card(card_one) + value_of_card(card_two):
|
||||
case 9 | 10 | 11:
|
||||
return True
|
||||
case _:
|
||||
return False
|
||||
@@ -0,0 +1,114 @@
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
from black_jack import (
|
||||
value_of_card,
|
||||
higher_card,
|
||||
value_of_ace,
|
||||
is_blackjack,
|
||||
can_split_pairs,
|
||||
can_double_down
|
||||
)
|
||||
|
||||
|
||||
class BlackJackTest(unittest.TestCase):
|
||||
|
||||
@pytest.mark.task(taskno=1)
|
||||
def test_value_of_card(self):
|
||||
test_data = [('2', 2), ('5', 5), ('8', 8),
|
||||
('A', 1), ('10', 10), ('J', 10),
|
||||
('Q', 10), ('K', 10)]
|
||||
|
||||
for variant, (card, expected) in enumerate(test_data, 1):
|
||||
with self.subTest(f'variation #{variant}', card=card, expected=expected):
|
||||
actual_result = value_of_card(card)
|
||||
error_msg = (f'Called value_of_card({card}). '
|
||||
f'The function returned {actual_result} as the value of the {card} card, '
|
||||
f'but the test expected {expected} as the {card} card value.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_msg)
|
||||
|
||||
|
||||
@pytest.mark.task(taskno=2)
|
||||
def test_higher_card(self):
|
||||
test_data = [('A', 'A', ('A', 'A')),
|
||||
('10', 'J', ('10', 'J')),
|
||||
('3', 'A', '3'),
|
||||
('3', '6', '6'),
|
||||
('Q', '10', ('Q', '10')),
|
||||
('4', '4', ('4', '4')),
|
||||
('9', '10', '10'),
|
||||
('6', '9', '9'),
|
||||
('4', '8', '8')]
|
||||
|
||||
for variant, (card_one, card_two, expected) in enumerate(test_data, 1):
|
||||
with self.subTest(f'variation #{variant}', card_one=card_one, card_two=card_two, expected=expected):
|
||||
actual_result = higher_card(card_one, card_two)
|
||||
error_msg = (f'Called higher_card({card_one}, {card_two}). '
|
||||
f'The function returned {actual_result}, '
|
||||
f'but the test expected {expected} as the result for the cards {card_one, card_two}.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_msg)
|
||||
|
||||
@pytest.mark.task(taskno=3)
|
||||
def test_value_of_ace(self):
|
||||
test_data = [('2', '3', 11), ('3', '6', 11), ('5', '2', 11),
|
||||
('8', '2', 11), ('5', '5', 11), ('Q', 'A', 1),
|
||||
('10', '2', 1), ('7', '8', 1), ('J', '9', 1),
|
||||
('K', 'K', 1), ('2', 'A', 1), ('A', '2', 1)]
|
||||
|
||||
for variant, (card_one, card_two, ace_value) in enumerate(test_data, 1):
|
||||
with self.subTest(f'variation #{variant}', card_one=card_one, card_two=card_two, ace_value=ace_value):
|
||||
actual_result = value_of_ace(card_one, card_two)
|
||||
error_msg = (f'Called value_of_ace({card_one}, {card_two}). '
|
||||
f'The function returned {actual_result}, '
|
||||
f'but the test expected {ace_value} as the value of an ace card '
|
||||
f'when the hand includes {card_one, card_two}.')
|
||||
|
||||
self.assertEqual(value_of_ace(card_one, card_two), ace_value, msg=error_msg)
|
||||
|
||||
@pytest.mark.task(taskno=4)
|
||||
def test_is_blackjack(self):
|
||||
test_data = [(('A', 'K'), True), (('10', 'A'), True),
|
||||
(('10', '9'), False), (('A', 'A'), False),
|
||||
(('4', '7'), False), (('9', '2'), False),
|
||||
(('Q', 'K'), False)]
|
||||
|
||||
for variant, (hand, expected) in enumerate(test_data, 1):
|
||||
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
|
||||
actual_result = is_blackjack(*hand)
|
||||
error_msg = (f'Called is_blackjack({hand[0]}, {hand[1]}). '
|
||||
f'The function returned {actual_result}, '
|
||||
f'but hand {hand} {"is" if expected else "is not"} a blackjack.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_msg)
|
||||
|
||||
@pytest.mark.task(taskno=5)
|
||||
def test_can_split_pairs(self):
|
||||
test_data = [(('Q', 'K'), True), (('6', '6'), True),
|
||||
(('A', 'A'), True),(('10', 'A'), False),
|
||||
(('10', '9'), False)]
|
||||
|
||||
for variant, (hand, expected) in enumerate(test_data, 1):
|
||||
with self.subTest(f'variation #{variant}', input=hand, expected=expected):
|
||||
actual_result = can_split_pairs(*hand)
|
||||
error_msg = (f'Called can_split_pairs({hand[0]}, {hand[1]}). '
|
||||
f'The function returned {actual_result}, '
|
||||
f'but hand {hand} {"can" if expected else "cannot"} be split into pairs.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_msg)
|
||||
|
||||
@pytest.mark.task(taskno=6)
|
||||
def test_can_double_down(self):
|
||||
test_data = [(('A', '9'), True), (('K', 'A'), True),
|
||||
(('4', '5'), True),(('A', 'A'), False),
|
||||
(('10', '2'), False), (('10', '9'), False)]
|
||||
|
||||
for variant, (hand, expected) in enumerate(test_data, 1):
|
||||
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
|
||||
actual_result = can_double_down(*hand)
|
||||
error_msg = (f'Called can_double_down({hand[0]}, {hand[1]}). '
|
||||
f'The function returned {actual_result}, '
|
||||
f'but hand {hand} {"can" if expected else "cannot"} be doubled down.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_msg)
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"authors": [],
|
||||
"contributors": [
|
||||
"0xae",
|
||||
"aldraco",
|
||||
"atg-abhishek",
|
||||
"austinlyons",
|
||||
"behrtam",
|
||||
"BethanyG",
|
||||
"cmccandless",
|
||||
"cypher",
|
||||
"Dog",
|
||||
"etrepum",
|
||||
"ikhadykin",
|
||||
"janetriley",
|
||||
"jremmen",
|
||||
"koljakube",
|
||||
"kytrinyx",
|
||||
"lowks",
|
||||
"lucasdpau",
|
||||
"miketamis",
|
||||
"N-Parsons",
|
||||
"patricksjackson",
|
||||
"pheanex",
|
||||
"sdublish",
|
||||
"sjakobi",
|
||||
"stonemirror",
|
||||
"thenigan",
|
||||
"thomasjpfan",
|
||||
"tqa236",
|
||||
"yawpitch"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"bob.py"
|
||||
],
|
||||
"test": [
|
||||
"bob_test.py"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.py"
|
||||
]
|
||||
},
|
||||
"blurb": "Bob is a lackadaisical teenager. In conversation, his responses are very limited.",
|
||||
"source": "Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial.",
|
||||
"source_url": "https://pine.fm/LearnToProgram/?Chapter=06"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"bob","id":"c2728ecca3df4966b10c928d9fdc8a06","url":"https://exercism.org/tracks/python/exercises/bob","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit bob.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Bob
|
||||
|
||||
Welcome to Bob on Exercism's Python Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Introduction
|
||||
|
||||
Bob is a [lackadaisical][] teenager.
|
||||
He likes to think that he's very cool.
|
||||
And he definitely doesn't get excited about things.
|
||||
That wouldn't be cool.
|
||||
|
||||
When people talk to him, his responses are pretty limited.
|
||||
|
||||
[lackadaisical]: https://www.collinsdictionary.com/dictionary/english/lackadaisical
|
||||
|
||||
## Instructions
|
||||
|
||||
Your task is to determine what Bob will reply to someone when they say something to him or ask him a question.
|
||||
|
||||
Bob only ever answers one of five things:
|
||||
|
||||
- **"Sure."**
|
||||
This is his response if you ask him a question, such as "How are you?"
|
||||
The convention used for questions is that it ends with a question mark.
|
||||
- **"Whoa, chill out!"**
|
||||
This is his answer if you YELL AT HIM.
|
||||
The convention used for yelling is ALL CAPITAL LETTERS.
|
||||
- **"Calm down, I know what I'm doing!"**
|
||||
This is what he says if you yell a question at him.
|
||||
- **"Fine. Be that way!"**
|
||||
This is how he responds to silence.
|
||||
The convention used for silence is nothing, or various combinations of whitespace characters.
|
||||
- **"Whatever."**
|
||||
This is what he answers to anything else.
|
||||
|
||||
## Source
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @0xae
|
||||
- @aldraco
|
||||
- @atg-abhishek
|
||||
- @austinlyons
|
||||
- @behrtam
|
||||
- @BethanyG
|
||||
- @cmccandless
|
||||
- @cypher
|
||||
- @Dog
|
||||
- @etrepum
|
||||
- @ikhadykin
|
||||
- @janetriley
|
||||
- @jremmen
|
||||
- @koljakube
|
||||
- @kytrinyx
|
||||
- @lowks
|
||||
- @lucasdpau
|
||||
- @miketamis
|
||||
- @N-Parsons
|
||||
- @patricksjackson
|
||||
- @pheanex
|
||||
- @sdublish
|
||||
- @sjakobi
|
||||
- @stonemirror
|
||||
- @thenigan
|
||||
- @thomasjpfan
|
||||
- @tqa236
|
||||
- @yawpitch
|
||||
|
||||
### Based on
|
||||
|
||||
Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. - https://pine.fm/LearnToProgram/?Chapter=06
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
def response(hey_bob):
|
||||
if hey_bob.strip() == "":
|
||||
return "Fine. Be that way!"
|
||||
elif hey_bob.strip().isupper() and hey_bob.strip()[-1] == "?":
|
||||
return "Calm down, I know what I'm doing!"
|
||||
elif hey_bob.strip()[-1] == "?":
|
||||
return "Sure."
|
||||
elif hey_bob.strip().isupper():
|
||||
return "Whoa, chill out!"
|
||||
else:
|
||||
return "Whatever."
|
||||
@@ -0,0 +1,101 @@
|
||||
# These tests are auto-generated with test data from:
|
||||
# https://github.com/exercism/problem-specifications/tree/main/exercises/bob/canonical-data.json
|
||||
# File last updated on 2025-01-10
|
||||
|
||||
import unittest
|
||||
|
||||
from bob import (
|
||||
response,
|
||||
)
|
||||
|
||||
|
||||
class BobTest(unittest.TestCase):
|
||||
def test_stating_something(self):
|
||||
self.assertEqual(response("Tom-ay-to, tom-aaaah-to."), "Whatever.")
|
||||
|
||||
def test_shouting(self):
|
||||
self.assertEqual(response("WATCH OUT!"), "Whoa, chill out!")
|
||||
|
||||
def test_shouting_gibberish(self):
|
||||
self.assertEqual(response("FCECDFCAAB"), "Whoa, chill out!")
|
||||
|
||||
def test_asking_a_question(self):
|
||||
self.assertEqual(
|
||||
response("Does this cryogenic chamber make me look fat?"), "Sure."
|
||||
)
|
||||
|
||||
def test_asking_a_numeric_question(self):
|
||||
self.assertEqual(response("You are, what, like 15?"), "Sure.")
|
||||
|
||||
def test_asking_gibberish(self):
|
||||
self.assertEqual(response("fffbbcbeab?"), "Sure.")
|
||||
|
||||
def test_talking_forcefully(self):
|
||||
self.assertEqual(response("Hi there!"), "Whatever.")
|
||||
|
||||
def test_using_acronyms_in_regular_speech(self):
|
||||
self.assertEqual(
|
||||
response("It's OK if you don't want to go work for NASA."), "Whatever."
|
||||
)
|
||||
|
||||
def test_forceful_question(self):
|
||||
self.assertEqual(
|
||||
response("WHAT'S GOING ON?"), "Calm down, I know what I'm doing!"
|
||||
)
|
||||
|
||||
def test_shouting_numbers(self):
|
||||
self.assertEqual(response("1, 2, 3 GO!"), "Whoa, chill out!")
|
||||
|
||||
def test_no_letters(self):
|
||||
self.assertEqual(response("1, 2, 3"), "Whatever.")
|
||||
|
||||
def test_question_with_no_letters(self):
|
||||
self.assertEqual(response("4?"), "Sure.")
|
||||
|
||||
def test_shouting_with_special_characters(self):
|
||||
self.assertEqual(
|
||||
response("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"),
|
||||
"Whoa, chill out!",
|
||||
)
|
||||
|
||||
def test_shouting_with_no_exclamation_mark(self):
|
||||
self.assertEqual(response("I HATE THE DENTIST"), "Whoa, chill out!")
|
||||
|
||||
def test_statement_containing_question_mark(self):
|
||||
self.assertEqual(response("Ending with ? means a question."), "Whatever.")
|
||||
|
||||
def test_non_letters_with_question(self):
|
||||
self.assertEqual(response(":) ?"), "Sure.")
|
||||
|
||||
def test_prattling_on(self):
|
||||
self.assertEqual(response("Wait! Hang on. Are you going to be OK?"), "Sure.")
|
||||
|
||||
def test_silence(self):
|
||||
self.assertEqual(response(""), "Fine. Be that way!")
|
||||
|
||||
def test_prolonged_silence(self):
|
||||
self.assertEqual(response(" "), "Fine. Be that way!")
|
||||
|
||||
def test_alternate_silence(self):
|
||||
self.assertEqual(response("\t\t\t\t\t\t\t\t\t\t"), "Fine. Be that way!")
|
||||
|
||||
def test_starting_with_whitespace(self):
|
||||
self.assertEqual(response(" hmmmmmmm..."), "Whatever.")
|
||||
|
||||
def test_ending_with_whitespace(self):
|
||||
self.assertEqual(
|
||||
response("Okay if like my spacebar quite a bit? "), "Sure."
|
||||
)
|
||||
|
||||
def test_other_whitespace(self):
|
||||
self.assertEqual(response("\n\r \t"), "Fine. Be that way!")
|
||||
|
||||
def test_non_question_ending_with_whitespace(self):
|
||||
self.assertEqual(
|
||||
response("This is a statement ending with whitespace "), "Whatever."
|
||||
)
|
||||
|
||||
def test_multiple_line_question(self):
|
||||
self.assertEqual(
|
||||
response("\nDoes this cryogenic chamber make\n me look fat?"), "Sure."
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"authors": [
|
||||
"itamargal",
|
||||
"isaacg",
|
||||
"bethanyg"
|
||||
],
|
||||
"contributors": [
|
||||
"valentin-p",
|
||||
"pranasziaukas"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"lists.py"
|
||||
],
|
||||
"test": [
|
||||
"lists_test.py"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.py"
|
||||
]
|
||||
},
|
||||
"icon": "poker",
|
||||
"blurb": "Learn about lists by tracking hands in card games."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"card-games","id":"06eba203367748c4996e631edfe7d087","url":"https://exercism.org/tracks/python/exercises/card-games","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit lists.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
## 1. Tracking Poker Rounds
|
||||
|
||||
- Lists in Python may be [constructed][constructed] in multiple ways.
|
||||
- This function should [return][return] a `list`.
|
||||
|
||||
## 2. Keeping all Rounds in the Same Place
|
||||
|
||||
- Sequence types such as `list` support [common operations][common sequence operations].
|
||||
- This function should [return][return] a `list`.
|
||||
|
||||
## 3. Finding Prior Rounds
|
||||
|
||||
- Sequence types such as `list` support a few [common operations][common sequence operations].
|
||||
- This function should [return][return] a `bool`.
|
||||
|
||||
## 4. Averaging Card Values
|
||||
|
||||
- To get the average, this function should count how many items are in the `list` and sum up their values. Then, return the sum divided by the count.
|
||||
|
||||
## 5. Alternate Averages
|
||||
|
||||
- Sequence types such as `list` support a few [common operations][common sequence operations].
|
||||
- To access an element, use the square brackets (`<list>[]`) notation.
|
||||
- Remember that the first element of the `list` is at index 0 from the **left-hand** side.
|
||||
- In Python, negative indexing starts at -1 from the **right-hand** side. This means that you can find the last element of a `list` by using `<list>[-1]`.
|
||||
- Think about how you could reuse the code from the functions that you have already implemented.
|
||||
|
||||
## 6. More Averaging Techniques
|
||||
|
||||
- Sequence types such as `list` already support a few [common operations][common sequence operations].
|
||||
- Think about reusing the code from the functions that you just implemented.
|
||||
- The slice syntax supports a _step value_ (`<list>[<start>:<stop>:<step>]`).
|
||||
|
||||
## 7. Bonus Round Rules
|
||||
|
||||
- Lists are _mutable_. Once a `list` is created, you can modify, delete or add any type of element you wish.
|
||||
- Python provides a wide range of [ways to modify `lists`][ways to modify `lists`].
|
||||
|
||||
|
||||
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
|
||||
[constructed]: https://docs.python.org/3/library/stdtypes.html#list
|
||||
[return]: https://www.w3schools.com/python/ref_keyword_return.asp
|
||||
[ways to modify `lists`]: https://realpython.com/python-lists-tuples/#lists-are-mutable
|
||||
@@ -0,0 +1,393 @@
|
||||
# Card Games
|
||||
|
||||
Welcome to Card Games on Exercism's Python 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 [`list`][list] is a mutable collection of items in _sequence_.
|
||||
Like most collections (_see the built-ins [`tuple`][tuple], [`dict`][dict] and [`set`][set]_), lists can hold reference to any (or multiple) data type(s) - including other lists.
|
||||
Like any [sequence][sequence type], items can be accessed via `0-based index` number from the left and `-1-based index` from the right.
|
||||
Lists can be copied in whole or in part via [slice notation][slice notation] or `<list>.copy()`.
|
||||
|
||||
Lists support both [common][common sequence operations] and [mutable][mutable sequence operations] sequence operations such as `min()`/`max()`, `<list>.index()`, `<list>.append()` and `<list>.reverse()`.
|
||||
List elements can be iterated over using the `for item in <list>` construct.
|
||||
`for index, item in enumerate(<list>)` can be used when both the element index and the element value are needed.
|
||||
|
||||
Under the hood, `lists` are implemented as [dynamic arrays][dynamic array] -- similar to Java's [`ArrayList`][arraylist] type, and are most often used to store groups of similar data (_strings, numbers, sets etc._) of unknown length.
|
||||
Lists are an extremely flexible and useful data structure and many built-in methods and operations in Python produce lists as their output.
|
||||
|
||||
|
||||
## Construction
|
||||
|
||||
A `list` can be declared as a _literal_ with square `[]` brackets and commas between elements:
|
||||
|
||||
|
||||
```python
|
||||
>>> no_elements = []
|
||||
|
||||
>>> no_elements
|
||||
[]
|
||||
|
||||
>>> one_element = ["Guava"]
|
||||
|
||||
>>> one_element
|
||||
['Guava']
|
||||
|
||||
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
|
||||
|
||||
>>> elements_separated_with_commas
|
||||
['Parrot', 'Bird', 334782]
|
||||
```
|
||||
|
||||
For readability, line breaks can be used when there are many elements or nested data structures within a `list`:
|
||||
|
||||
|
||||
```python
|
||||
>>> lots_of_entries = [
|
||||
"Rose",
|
||||
"Sunflower",
|
||||
"Poppy",
|
||||
"Pansy",
|
||||
"Tulip",
|
||||
"Fuchsia",
|
||||
"Cyclamen",
|
||||
"Lavender"
|
||||
]
|
||||
|
||||
>>> lots_of_entries
|
||||
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']
|
||||
|
||||
# Each data structure is on its own line to help clarify what they are.
|
||||
>>> nested_data_structures = [
|
||||
{"fish": "gold", "monkey": "brown", "parrot": "grey"},
|
||||
("fish", "mammal", "bird"),
|
||||
['water', 'jungle', 'sky']
|
||||
]
|
||||
|
||||
>>> nested_data_structures
|
||||
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]
|
||||
```
|
||||
|
||||
The `list()` constructor can be used empty or with an _iterable_ as an argument.
|
||||
Elements in the iterable are cycled through by the constructor and added to the `list` in order:
|
||||
|
||||
|
||||
```python
|
||||
>>> no_elements = list()
|
||||
|
||||
>>> no_elements
|
||||
[]
|
||||
|
||||
# The tuple is unpacked and each element is added.
|
||||
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))
|
||||
|
||||
>>> multiple_elements_from_tuple
|
||||
['Parrot', 'Bird', 334782]
|
||||
|
||||
# The set is unpacked and each element is added.
|
||||
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})
|
||||
|
||||
>>> multiple_elements_from_set
|
||||
[2, 3, 5, 7, 11]
|
||||
```
|
||||
|
||||
Results when using a `list` constructor with a `string` or a `dict` may be surprising:
|
||||
|
||||
|
||||
```python
|
||||
# String elements (Unicode code points) are iterated through and added *individually*.
|
||||
>>> multiple_elements_string = list("Timbuktu")
|
||||
|
||||
>>> multiple_elements_string
|
||||
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']
|
||||
|
||||
# Unicode separators and positioning code points are also added *individually*.
|
||||
>>> multiple_code_points_string = list('अभ्यास')
|
||||
|
||||
>>> multiple_code_points_string
|
||||
['अ', 'भ', '्', 'य', 'ा', 'स']
|
||||
|
||||
# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
|
||||
>>> source_data = {"fish": "gold", "monkey": "brown"}
|
||||
|
||||
>>> multiple_elements_dict_1 = list(source_data)
|
||||
['fish', 'monkey']
|
||||
```
|
||||
|
||||
Because the `list` constructor will only take _iterables_ (or nothing) as arguments, objects that are _not_ iterable will throw a type error.
|
||||
Consequently, it is much easier to create a one-item `list` via the literal method.
|
||||
|
||||
```python
|
||||
# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
|
||||
>>> one_element = list(16)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: 'int' object is not iterable
|
||||
|
||||
# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
|
||||
>>> one_element_from_iterable = list((16,))
|
||||
|
||||
>>> one_element_from_iterable
|
||||
[16]
|
||||
```
|
||||
|
||||
## Accessing elements
|
||||
|
||||
Items inside lists (_as well as items in other sequence types `str` & `tuple`_) can be accessed via `0-based index` and _bracket notation_.
|
||||
Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right`** --> **`left`** (_starting at -1_).
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td style="vertical-align: top"> index from left ⟹<br><br><br><br><br><br><br></td><td style="vertical-align: middle">
|
||||
|
||||
| 0<br>👇🏾 | 1<br>👇🏾 | 2<br>👇🏾 | 3<br>👇🏾 | 4<br>👇🏾 | 5<br>👇🏾 |
|
||||
|:--------: |:--------: |:--------: |:--------: |:--------: |:--------: |
|
||||
| P | y | t | h | o | n |
|
||||
| 👆🏾<br>-6 | 👆🏾<br>-5 | 👆🏾<br>-4 | 👆🏾<br>-3 | 👆🏾<br>-2 | 👆🏾<br>-1 |
|
||||
</td><td style="vertical-align: bottom"><br><br><br><br><br>⟸ index from right</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
```python
|
||||
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]
|
||||
|
||||
# Oatmeal is at index 0 or index -4.
|
||||
>>> breakfast_foods[0]
|
||||
'Oatmeal'
|
||||
|
||||
>>> breakfast_foods[-4]
|
||||
'Oatmeal'
|
||||
|
||||
# Eggs are at index -2 or 2
|
||||
>>> breakfast_foods[-2]
|
||||
'Eggs'
|
||||
|
||||
>>> breakfast_foods[2]
|
||||
'Eggs'
|
||||
|
||||
# Toast is at -1
|
||||
>>> breakfast_foods[-1]
|
||||
'Toast'
|
||||
```
|
||||
|
||||
A section of the elements inside a `list` can be accessed via _slice notation_ (`<list>[start:stop]`).
|
||||
A _slice_ is defined as an element sequence at position `index`, such that `start <= index < stop`.
|
||||
_Slicing_ returns a copy of the "sliced" items and does not modify the original `list`.
|
||||
|
||||
|
||||
A `step` parameter can also be used `[start:stop:step]` to "skip over" or filter the `list` elements (_for example, a `step` of 2 will select every other element in the range_):
|
||||
|
||||
|
||||
```python
|
||||
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
|
||||
|
||||
# If there is no step parameter, the step is assumed to be 1.
|
||||
>>> middle_colors = colors[2:6]
|
||||
|
||||
>>> middle_colors
|
||||
['Green', 'Yellow', 'Orange', 'Pink']
|
||||
|
||||
# If the start or stop parameters are omitted, the slice will
|
||||
# start at index zero, and will stop at the end of the list.
|
||||
>>> primary_colors = colors[::3]
|
||||
|
||||
>>> primary_colors
|
||||
['Red', 'Yellow', 'Blue']
|
||||
```
|
||||
|
||||
## Working with lists
|
||||
|
||||
The usage of the built-in `sum()` function on a list will return the sum of all the numbers in the list:
|
||||
|
||||
```python
|
||||
>>> number_list = [1, 2, 3, 4]
|
||||
>>> sum(number_list)
|
||||
10
|
||||
```
|
||||
|
||||
You can also get the _length_ of a list by using the `len()` function:
|
||||
|
||||
```python
|
||||
>>> long_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
|
||||
>>> len(long_list)
|
||||
10
|
||||
```
|
||||
|
||||
Lists can be also combined in various ways:
|
||||
|
||||
```python
|
||||
# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
|
||||
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]
|
||||
|
||||
>>> new_via_concatenate
|
||||
['George', 5, 'cat', 'Tabby']
|
||||
|
||||
# Likewise, using the multiplication operator * is the equivalent of using + n times.
|
||||
>>> first_group = ["cat", "dog", "elephant"]
|
||||
>>> multiplied_group = first_group * 3
|
||||
|
||||
>>> multiplied_group
|
||||
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']
|
||||
```
|
||||
|
||||
Lists supply an _iterator_, and can be looped through/over in the same manner as other _sequence types_.
|
||||
|
||||
```python
|
||||
# Looping through the list and printing out each element.
|
||||
>>> colors = ["Orange", "Green", "Grey", "Blue"]
|
||||
|
||||
>>> for item in colors:
|
||||
... print(item)
|
||||
...
|
||||
Orange
|
||||
Green
|
||||
Grey
|
||||
Blue
|
||||
```
|
||||
|
||||
_For a more in-depth explanation, of `loops` and `iterators`, complete the `loops` concept._
|
||||
|
||||
[arraylist]: https://beginnersbook.com/2013/12/java-arraylist/
|
||||
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
|
||||
[dict]: https://docs.python.org/3/library/stdtypes.html#dict
|
||||
[dynamic array]: https://en.wikipedia.org/wiki/Dynamic_array
|
||||
[list]: https://docs.python.org/3/library/stdtypes.html#list
|
||||
[mutable sequence operations]: https://docs.python.org/3/library/stdtypes.html#typesseq-mutable
|
||||
[sequence type]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
|
||||
[set]: https://docs.python.org/3/library/stdtypes.html#set
|
||||
[slice notation]: https://docs.python.org/3/reference/expressions.html#slicings
|
||||
[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
|
||||
|
||||
## Instructions
|
||||
|
||||
Elyse is really looking forward to playing some poker (and other card games) during her upcoming trip to Vegas.
|
||||
Being a big fan of "self-tracking" she wants to put together some small functions that will help her with tracking tasks and has asked for your help thinking them through.
|
||||
|
||||
## 1. Tracking Poker Rounds
|
||||
|
||||
Elyse is especially fond of poker, and wants to track how many rounds she plays - and _which rounds_ those are.
|
||||
Every round has its own number, and every table shows the round number currently being played.
|
||||
Elyse chooses a table and sits down to play her first round. She plans on playing three rounds.
|
||||
|
||||
Implement a function `get_rounds(<round_number>)` that takes the current round number and returns a single `list` with that round and the _next two_ that are coming up:
|
||||
|
||||
```python
|
||||
>>> get_rounds(27)
|
||||
[27, 28, 29]
|
||||
```
|
||||
|
||||
## 2. Keeping all Rounds in the Same Place
|
||||
|
||||
Elyse played a few rounds at the first table, then took a break and played some more rounds at a second table ... but ended up with a different list for each table!
|
||||
She wants to put the two lists together, so she can track all of the poker rounds in the same place.
|
||||
|
||||
Implement a function `concatenate_rounds(<rounds_1>, <rounds_2>)` that takes two lists and returns a single `list` consisting of all the rounds in the first `list`, followed by all the rounds in the second `list`:
|
||||
|
||||
```python
|
||||
>>> concatenate_rounds([27, 28, 29], [35, 36])
|
||||
[27, 28, 29, 35, 36]
|
||||
```
|
||||
|
||||
## 3. Finding Prior Rounds
|
||||
|
||||
Talking about some of the prior Poker rounds, another player remarks how similarly two of them played out.
|
||||
Elyse is not sure if she played those rounds or not.
|
||||
|
||||
Implement a function `list_contains_round(<rounds>, <round_number>)` that takes two arguments, a list of rounds played and a round number.
|
||||
The function will return `True` if the round is in the list of rounds played, `False` if not:
|
||||
|
||||
```python
|
||||
>>> list_contains_round([27, 28, 29, 35, 36], 29)
|
||||
True
|
||||
|
||||
>>> list_contains_round([27, 28, 29, 35, 36], 30)
|
||||
False
|
||||
```
|
||||
|
||||
## 4. Averaging Card Values
|
||||
|
||||
Elyse wants to try out a new game called Black Joe.
|
||||
It's similar to Black Jack - where your goal is to have the cards in your hand add up to a target value - but in Black Joe the goal is to get the _average_ of the card values to be 7.
|
||||
The average can be found by summing up all the card values and then dividing that sum by the number of cards in the hand.
|
||||
|
||||
Implement a function `card_average(<hand>)` that will return the average value of a hand of Black Joe.
|
||||
|
||||
```python
|
||||
>>> card_average([5, 6, 7])
|
||||
6.0
|
||||
```
|
||||
|
||||
## 5. Alternate Averages
|
||||
|
||||
In Black Joe, speed is important. Elyse is going to try and find a faster way of finding the average.
|
||||
|
||||
She has thought of two ways of getting an _average-like_ number:
|
||||
|
||||
- Take the average of the _first_ and _last_ number in the hand.
|
||||
- Using the median (middle card) of the hand.
|
||||
|
||||
Implement the function `approx_average_is_average(<hand>)`, given `hand`, a list containing the values of the cards in your hand.
|
||||
|
||||
Return `True` if either _one_ `or` _both_ of the, above named, strategies result in a number _equal_ to the _actual average_.
|
||||
|
||||
Note: _The length of all hands are odd, to make finding a median easier._
|
||||
|
||||
```python
|
||||
>>> approx_average_is_average([1, 2, 3])
|
||||
True
|
||||
|
||||
>>> approx_average_is_average([2, 3, 4, 8, 8])
|
||||
True
|
||||
|
||||
>>> approx_average_is_average([1, 2, 3, 5, 9])
|
||||
False
|
||||
```
|
||||
|
||||
## 6. More Averaging Techniques
|
||||
|
||||
Intrigued by the results of her averaging experiment, Elyse is wondering if taking the average of the cards at the _even_ positions versus the average of the cards at the _odd_ positions would give the same results.
|
||||
Time for another test function!
|
||||
|
||||
Implement a function `average_even_is_average_odd(<hand>)` that returns a Boolean indicating if the average of the cards at even indexes is the same as the average of the cards at odd indexes.
|
||||
|
||||
```python
|
||||
>>> average_even_is_average_odd([1, 2, 3])
|
||||
True
|
||||
|
||||
>>> average_even_is_average_odd([1, 2, 3, 4])
|
||||
False
|
||||
```
|
||||
|
||||
## 7. Bonus Round Rules
|
||||
|
||||
Every 11th hand in Black Joe is a bonus hand with a bonus rule: if the last card you draw is a Jack, you double its value.
|
||||
|
||||
Implement a function `maybe_double_last(<hand>)` that takes a hand and checks if the last card is a Jack (11).
|
||||
If the last card **is** a Jack (11), double its value before returning the hand.
|
||||
|
||||
```python
|
||||
>>> hand = [5, 9, 11]
|
||||
>>> maybe_double_last(hand)
|
||||
[5, 9, 22]
|
||||
|
||||
>>> hand = [5, 9, 10]
|
||||
>>> maybe_double_last(hand)
|
||||
[5, 9, 10]
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @itamargal
|
||||
- @isaacg
|
||||
- @bethanyg
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @valentin-p
|
||||
- @pranasziaukas
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,96 @@
|
||||
"""Functions for tracking poker hands and assorted card tasks.
|
||||
|
||||
Python list documentation: https://docs.python.org/3/tutorial/datastructures.html
|
||||
"""
|
||||
|
||||
|
||||
def get_rounds(number):
|
||||
"""Create a list containing the current and next two round numbers.
|
||||
|
||||
:param number: int - current round number.
|
||||
:return: list - current round and the two that follow.
|
||||
"""
|
||||
return [number + next for next in range(0, 3)]
|
||||
|
||||
|
||||
def concatenate_rounds(rounds_1, rounds_2):
|
||||
"""Concatenate two lists of round numbers.
|
||||
|
||||
:param rounds_1: list - first rounds played.
|
||||
:param rounds_2: list - second set of rounds played.
|
||||
:return: list - all rounds played.
|
||||
"""
|
||||
new_list: list = []
|
||||
new_list.extend(rounds_1)
|
||||
new_list.extend(rounds_2)
|
||||
return new_list
|
||||
|
||||
|
||||
def list_contains_round(rounds, number):
|
||||
"""Check if the list of rounds contains the specified number.
|
||||
|
||||
:param rounds: list - rounds played.
|
||||
:param number: int - round number.
|
||||
:return: bool - was the round played?
|
||||
"""
|
||||
return number in rounds
|
||||
|
||||
|
||||
def card_average(hand):
|
||||
"""Calculate and returns the average card value from the list.
|
||||
|
||||
:param hand: list - cards in hand.
|
||||
:return: float - average value of the cards in the hand.
|
||||
"""
|
||||
total: int = 0
|
||||
for card in hand:
|
||||
total += card
|
||||
return total / len(hand)
|
||||
|
||||
|
||||
def approx_average_is_average(hand):
|
||||
"""Return if the (average of first and last card values) OR ('middle' card) == calculated average.
|
||||
|
||||
:param hand: list - cards in hand.
|
||||
:return: bool - does one of the approximate averages equal the `true average`?
|
||||
"""
|
||||
average_first_and_last: float = (hand[0] + hand[-1]) / 2
|
||||
middle_card: int = hand[len(hand) // 2]
|
||||
return average_first_and_last == card_average(hand) or middle_card == card_average(
|
||||
hand
|
||||
)
|
||||
|
||||
|
||||
def average_even_is_average_odd(hand):
|
||||
"""Return if the (average of even indexed card values) == (average of odd indexed card values).
|
||||
|
||||
:param hand: list - cards in hand.
|
||||
:return: bool - are even and odd averages equal?
|
||||
"""
|
||||
total_even: float = 0.00
|
||||
count_even: int = 0
|
||||
total_odd: float = 0.00
|
||||
count_odd: int = 0
|
||||
for index, card in enumerate(hand):
|
||||
if index % 2:
|
||||
total_even += card
|
||||
count_even += 1
|
||||
else:
|
||||
total_odd += card
|
||||
count_odd += 1
|
||||
return (total_even / count_even) == (total_odd / count_odd)
|
||||
|
||||
|
||||
def maybe_double_last(hand):
|
||||
"""Multiply a Jack card value in the last index position by 2.
|
||||
|
||||
:param hand: list - cards in hand.
|
||||
:return: list - hand with Jacks (if present) value doubled.
|
||||
"""
|
||||
new_list: list = []
|
||||
for index, card in enumerate(hand):
|
||||
if card == 11 and index == len(hand) - 1:
|
||||
new_list.append(card * 2)
|
||||
else:
|
||||
new_list.append(card)
|
||||
return new_list
|
||||
@@ -0,0 +1,137 @@
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
from lists import (
|
||||
get_rounds,
|
||||
concatenate_rounds,
|
||||
list_contains_round,
|
||||
card_average,
|
||||
approx_average_is_average,
|
||||
average_even_is_average_odd,
|
||||
maybe_double_last,
|
||||
)
|
||||
|
||||
|
||||
class CardGamesTest(unittest.TestCase):
|
||||
|
||||
@pytest.mark.task(taskno=1)
|
||||
def test_get_rounds(self):
|
||||
|
||||
input_data = [0, 1, 10, 27, 99, 666]
|
||||
result_data = [[0, 1, 2], [1, 2, 3],
|
||||
[10, 11, 12], [27, 28, 29],
|
||||
[99, 100, 101], [666, 667, 668]]
|
||||
|
||||
for variant, (number, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||
with self.subTest(f'variation #{variant}', number=number, expected=expected):
|
||||
actual_result = get_rounds(number)
|
||||
error_message = (f'Called get_rounds({number}). '
|
||||
f'The function returned {actual_result}, '
|
||||
f'but the tests expected rounds {expected} '
|
||||
f'given the current round {number}.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=2)
|
||||
def test_concatenate_rounds(self):
|
||||
|
||||
input_data = [([], []), ([0, 1], []), ([], [1, 2]),
|
||||
([1], [2]), ([27, 28, 29], [35, 36]),
|
||||
([1, 2, 3], [4, 5, 6])]
|
||||
|
||||
result_data = [[], [0, 1], [1, 2], [1, 2],
|
||||
[27, 28, 29, 35, 36],
|
||||
[1, 2, 3, 4, 5, 6]]
|
||||
|
||||
for variant, ((rounds_1, rounds_2), expected) in enumerate(zip(input_data, result_data), start=1):
|
||||
with self.subTest(f'variation #{variant}', rounds_1=rounds_1, rounds_2=rounds_2, expected=expected):
|
||||
actual_result = concatenate_rounds(rounds_1, rounds_2)
|
||||
error_message = (f'Called concatenate_rounds({rounds_1}, {rounds_2}). '
|
||||
f'The function returned {actual_result}, but the tests '
|
||||
f'expected {expected} as the concatenation '
|
||||
f'of {rounds_1} and {rounds_2}.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=3)
|
||||
def test_list_contains_round(self):
|
||||
|
||||
input_data = [([], 1), ([1, 2, 3], 0),
|
||||
([27, 28, 29, 35, 36], 30),
|
||||
([1], 1), ([1, 2, 3], 1),
|
||||
([27, 28, 29, 35, 36], 29)]
|
||||
result_data = [False, False, False, True, True, True]
|
||||
|
||||
for variant, ((rounds, round_number), expected) in enumerate(zip(input_data, result_data), start=1):
|
||||
with self.subTest(f'variation #{variant}', rounds=rounds, round_number=round_number, expected=expected):
|
||||
actual_result = list_contains_round(rounds, round_number)
|
||||
error_message = (f'Called list_contains_round({rounds}, {round_number}). '
|
||||
f'The function returned {actual_result}, but round {round_number} '
|
||||
f'{"is" if expected else "is not"} in {rounds}.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=4)
|
||||
def test_card_average(self):
|
||||
|
||||
input_data = [[1], [5, 6, 7], [1, 2, 3, 4], [1, 10, 100]]
|
||||
result_data = [1.0, 6.0, 2.5, 37.0]
|
||||
|
||||
for variant, (hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
|
||||
actual_result = card_average(hand)
|
||||
error_message = (f'Called card_average({hand}). '
|
||||
f'The function returned {actual_result}, but '
|
||||
f'the tests expected {expected} as the average of {hand}.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=5)
|
||||
def test_approx_average_is_average(self):
|
||||
|
||||
input_data = [[0, 1, 5], [3, 6, 9, 12, 150], [1, 2, 3, 5, 9],
|
||||
[2, 3, 4, 7, 8], [1, 2, 3], [2, 3, 4],
|
||||
[2, 3, 4, 8, 8], [1, 2, 4, 5, 8]]
|
||||
|
||||
result_data = [False, False, False, False, True, True, True, True]
|
||||
|
||||
for variant, (hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
|
||||
actual_result = approx_average_is_average(hand)
|
||||
error_message = (f'Called approx_average_is_average({hand}). '
|
||||
f'The function returned {actual_result}, but '
|
||||
f'the hand {hand} {"does" if expected else "does not"} '
|
||||
f'yield the same approximate average.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=6)
|
||||
def test_average_even_is_average_odd(self):
|
||||
|
||||
input_data = [[5, 6, 8], [1, 2, 3, 4], [1, 2, 3], [5, 6, 7], [1, 3, 5, 7, 9]]
|
||||
result_data = [False, False, True, True, True]
|
||||
|
||||
for variant, (input_hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||
with self.subTest(f'variation #{variant}', input_hand=input_hand, expected=expected):
|
||||
actual_result = average_even_is_average_odd(input_hand)
|
||||
error_message = (f'Called average_even_is_average_odd({input_hand}). '
|
||||
f'The function returned {actual_result}, but '
|
||||
f'the hand {"does" if expected else "does not"} '
|
||||
f'yield the same odd-even average.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=7)
|
||||
def test_maybe_double_last(self):
|
||||
|
||||
input_data = [(1, 2, 11), (5, 9, 11), (5, 9, 10), (1, 2, 3), (1, 11, 8)]
|
||||
result_data = [[1, 2, 22], [5, 9, 22], [5, 9, 10], [1, 2, 3], [1, 11, 8]]
|
||||
|
||||
for variant, (hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||
with self.subTest(f'variation #{variant}', hand=list(hand), expected=expected):
|
||||
actual_result = maybe_double_last(list(hand))
|
||||
error_message = (f'Called maybe_double_last({list(hand)}). '
|
||||
f'The function returned {actual_result}, but '
|
||||
f'the tests expected {expected} as the maybe-doubled version of {list(hand)}.')
|
||||
|
||||
self.assertEqual(actual_result, expected, msg=error_message)
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"authors": [
|
||||
"bethanyg"
|
||||
],
|
||||
"contributors": [
|
||||
"zepam"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"sets.py"
|
||||
],
|
||||
"test": [
|
||||
"sets_test.py"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.py"
|
||||
],
|
||||
"editor": [
|
||||
"sets_test_data.py",
|
||||
"sets_categories_data.py"
|
||||
]
|
||||
},
|
||||
"icon": "meetup",
|
||||
"blurb": "Learn about sets by managing the menus and ingredients for your catering company's event."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"cater-waiter","id":"4bfa6aa1746a4186ab33090d6f51b49e","url":"https://exercism.org/tracks/python/exercises/cater-waiter","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit sets.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- [Sets][sets] are mutable, unordered collections with no duplicate elements.
|
||||
- Sets can contain any data type, as long as all elements are [hashable][hashable].
|
||||
- Sets are [iterable][iterable].
|
||||
- Sets are most often used to quickly dedupe other collections or for membership testing.
|
||||
- Sets also support mathematical operations like `union`, `intersection`, `difference`, and `symmetric difference`
|
||||
|
||||
## 1. Clean up Dish Ingredients
|
||||
|
||||
- The `set()` constructor can take any [iterable][iterable] as an argument. [concept: lists](/tracks/python/concepts/lists) are iterable.
|
||||
- Remember: [concept: tuples](/tracks/python/concepts/tuples) can be formed using `(<element_1>, <element_2>)` or via the `tuple()` constructor.
|
||||
|
||||
## 2. Cocktails and Mocktails
|
||||
|
||||
- A `set` is _disjoint_ from another set if the two sets share no elements.
|
||||
- The `set()` constructor can take any [iterable][iterable] as an argument. [concept: lists](/tracks/python/concepts/lists) are iterable.
|
||||
- In Python, [concept: strings](/tracks/python/concepts/strings) can be concatenated with the `+` sign.
|
||||
|
||||
## 3. Categorize Dishes
|
||||
|
||||
- Using [concept: loops](/tracks/python/concepts/loops) to iterate through the available meal categories might be useful here.
|
||||
- If all the elements of `<set_1>` are contained within `<set_2>`, then `<set_1> <= <set_2>`.
|
||||
- The method equivalent of `<=` is `<set>.issubset(<iterable>)`
|
||||
- [concept: tuples](/tracks/python/concepts/tuples) can contain any data type, including other tuples. Tuples can be formed using `(<element_1>, <element_2>)` or via the `tuple()` constructor.
|
||||
- Elements within [concept: tuples](/tracks/python/concepts/tuples) can be accessed from the left using a 0-based index number, or from the right using a -1-based index number.
|
||||
- The `set()` constructor can take any [iterable][iterable] as an argument. [concept: lists](/tracks/python/concepts/lists) are iterable.
|
||||
- [concept: strings](/tracks/python/concepts/strings) can be concatenated with the `+` sign.
|
||||
|
||||
## 4. Label Allergens and Restricted Foods
|
||||
|
||||
- A set _intersection_ are the elements shared between `<set_1>` and `<set_2>`.
|
||||
- The set method equivalent of `&` is `<set>.intersection(<iterable>)`
|
||||
- Elements within [concept: tuples](/tracks/python/concepts/tuples) can be accessed from the left using a 0-based index number, or from the right using a -1-based index number.
|
||||
- The `set()` constructor can take any [iterable][iterable] as an argument. [concept: lists](/tracks/python/concepts/lists) are iterable.
|
||||
- [concept: tuples](/tracks/python/concepts/tuples) can be formed using `(<element_1>, <element_2>)` or via the `tuple()` constructor.
|
||||
|
||||
## 5. Compile a "Master List" of Ingredients
|
||||
|
||||
- A set _union_ is where `<set_1`> and `<set_2>` are combined into a single `set`
|
||||
- The set method equivalent of `|` is `<set>.union(<iterable>)`
|
||||
- Using [concept: loops](/tracks/python/concepts/loops) to iterate through the various dishes might be useful here.
|
||||
|
||||
## 6. Pull out Appetizers for Passing on Trays
|
||||
|
||||
- A set _difference_ is where the elements of `<set_2>` are removed from `<set_1>`, e.g. `<set_1> - <set_2>`.
|
||||
- The set method equivalent of `-` is `<set>.difference(<iterable>)`
|
||||
- The `set()` constructor can take any [iterable][iterable] as an argument. [concept: lists](/tracks/python/concepts/lists) are iterable.
|
||||
- The [concept: list](/tracks/python/concepts/lists) constructor can take any [iterable][iterable] as an argument. Sets are iterable.
|
||||
|
||||
## 7. Find Ingredients Used in Only One Recipe
|
||||
|
||||
- A set _symmetric difference_ is where elements appear in `<set_1>` or `<set_2>`, but not **_both_** sets.
|
||||
- A set _symmetric difference_ is the same as subtracting the `set` _intersection_ from the `set` _union_, e.g. `(<set_1> | <set_2>) - (<set_1> & <set_2>)`
|
||||
- A _symmetric difference_ of more than two `sets` will include elements that are repeated more than two times across the input `sets`. To remove these cross-set repeated elements, the _intersections_ between set pairs needs to be subtracted from the symmetric difference.
|
||||
- Using [concept: loops](/tracks/python/concepts/loops) to iterate through the various dishes might be useful here.
|
||||
|
||||
|
||||
[hashable]: https://docs.python.org/3.7/glossary.html#term-hashable
|
||||
[iterable]: https://docs.python.org/3/glossary.html#term-iterable
|
||||
[sets]: https://docs.python.org/3/tutorial/datastructures.html#sets
|
||||
@@ -0,0 +1,582 @@
|
||||
# Cater Waiter
|
||||
|
||||
Welcome to Cater Waiter on Exercism's Python 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 [set][type-set] is a _mutable_ and _unordered_ collection of [_hashable_][hashable] objects.
|
||||
Set members must be distinct — duplicate items are not allowed.
|
||||
They can hold multiple different data types and even nested structures like a `tuple` of `tuples` — as long as all elements can be _hashed_.
|
||||
Sets also come in an immutable [`frozensets`][type-frozenset] flavor.
|
||||
|
||||
Sets are most commonly used to quickly remove duplicates from other data structures or item groupings.
|
||||
They are also used for efficient comparisons when sequencing and duplicate tracking are not needed.
|
||||
|
||||
Like other collection types (_dictionaries, lists, tuples_), `sets` support:
|
||||
- Iteration via `for item in <set>`
|
||||
- Membership checking via `in` and `not in`,
|
||||
- Length calculation through `len()`, and
|
||||
- Shallow copies through `copy()`
|
||||
|
||||
`sets` do not support:
|
||||
- Indexing of any kind
|
||||
- Ordering via sorting or insertion
|
||||
- Slicing
|
||||
- Concatenation via `+`
|
||||
|
||||
|
||||
Checking membership in a `set` has constant time complexity (on average) versus checking membership in a `list` or `string`, where the time complexity grows as the length of the data increases.
|
||||
Methods such as `<set>.union()`, `<set>.intersection()`, or `<set>.difference()` also have constant time complexity (on average).
|
||||
|
||||
|
||||
## Set Literals
|
||||
|
||||
A `set` can be directly entered as a _set literal_ with curly `{}` brackets and commas between elements.
|
||||
Duplicates are silently omitted:
|
||||
|
||||
```python
|
||||
>>> one_element = {'😀'}
|
||||
{'😀'}
|
||||
|
||||
>>> multiple_elements = {'😀', '😃', '😄', '😁'}
|
||||
{'😀', '😃', '😄', '😁'}
|
||||
|
||||
>>> multiple_duplicates = {'Hello!', 'Hello!', 'Hello!',
|
||||
'¡Hola!','Привіт!', 'こんにちは!',
|
||||
'¡Hola!','Привіт!', 'こんにちは!'}
|
||||
{'こんにちは!', '¡Hola!', 'Hello!', 'Привіт!'}
|
||||
```
|
||||
|
||||
Set literals use the same curly braces as `dict` literals, which means you need to use `set()` to create an empty `set`.
|
||||
|
||||
|
||||
## The Set Constructor
|
||||
|
||||
`set()` (_the constructor for the `set` class_) can be used with any `iterable` passed as an argument.
|
||||
Elements of the `iterable` are cycled through and added to the `set` individually.
|
||||
Element order is not preserved and duplicates are silently omitted:
|
||||
|
||||
```python
|
||||
# To create an empty set, the constructor must be used.
|
||||
>>> no_elements = set()
|
||||
set()
|
||||
|
||||
# The tuple is unpacked & each element is added.
|
||||
# Duplicates are removed.
|
||||
>>> elements_from_tuple = set(("Parrot", "Bird",
|
||||
334782, "Bird", "Parrot"))
|
||||
{334782, 'Bird', 'Parrot'}
|
||||
|
||||
# The list is unpacked & each element is added.
|
||||
# Duplicates are removed.
|
||||
>>> elements_from_list = set([2, 3, 2, 3, 3, 3, 5,
|
||||
7, 11, 7, 11, 13, 13])
|
||||
{2, 3, 5, 7, 11, 13}
|
||||
```
|
||||
|
||||
### Gotchas when Creating Sets
|
||||
|
||||
Due to its "unpacking" behavior, using `set()` with a string might be surprising:
|
||||
|
||||
```python
|
||||
# String elements (Unicode code points) are
|
||||
# iterated through and added *individually*.
|
||||
>>> elements_string = set("Timbuktu")
|
||||
{'T', 'b', 'i', 'k', 'm', 't', 'u'}
|
||||
|
||||
# Unicode separators and positioning code points
|
||||
# are also added *individually*.
|
||||
>>> multiple_code_points_string = set('अभ्यास')
|
||||
{'अ', 'भ', 'य', 'स', 'ा', '्'}
|
||||
```
|
||||
|
||||
Sets can hold different datatypes and _nested_ datatypes, but all `set` elements must be _hashable_:
|
||||
|
||||
```python
|
||||
# Attempting to use a list for a set member throws a TypeError
|
||||
>>> lists_as_elements = {['😅','🤣'],
|
||||
['😂','🙂','🙃'],
|
||||
['😜', '🤪', '😝']}
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: unhashable type: 'list'
|
||||
|
||||
|
||||
# Standard sets are mutable, so they cannot be hashed.
|
||||
>>> sets_as_elements = {{'😅','🤣'},
|
||||
{'😂','🙂','🙃'},
|
||||
{'😜', '🤪', '😝'}}
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: unhashable type: 'set'
|
||||
```
|
||||
|
||||
|
||||
## Working with Sets
|
||||
|
||||
Sets have methods that generally mimic [mathematical set operations][mathematical-sets].
|
||||
Most (_not all_) of these methods have an [operator][operator] equivalent.
|
||||
Methods generally take any `iterable` as an argument, while operators require that both sides of the operation are `sets` or `frozensets`.
|
||||
|
||||
|
||||
### Disjoint Sets
|
||||
|
||||
The `<set>.isdisjoint(<other_collection>)` method is used to test if a `sets` elements have any overlap with the elements of another `set`.
|
||||
The method will accept any `iterable` or `set` as an argument.
|
||||
It will return `True` if the two sets have **no elements in common**, `False` if elements are **shared**.
|
||||
There is no operator equivalent:
|
||||
|
||||
|
||||
```python
|
||||
# Both mammals and additional_animals are lists.
|
||||
>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
|
||||
>>> additional_animals = ['pangolin', 'panda', 'parrot',
|
||||
'lemur', 'tiger', 'pangolin']
|
||||
|
||||
# Animals is a dict.
|
||||
>>> animals = {'chicken': 'white',
|
||||
'sparrow': 'grey',
|
||||
'eagle': 'brown and white',
|
||||
'albatross': 'grey and white',
|
||||
'crow': 'black',
|
||||
'elephant': 'grey',
|
||||
'dog': 'rust',
|
||||
'cow': 'black and white',
|
||||
'tiger': 'orange and black',
|
||||
'cat': 'grey',
|
||||
'squirrel': 'black'}
|
||||
|
||||
# Birds is a set.
|
||||
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
|
||||
|
||||
# Mammals and birds don't share any elements.
|
||||
>>> birds.isdisjoint(mammals)
|
||||
True
|
||||
|
||||
# There are also no shared elements between
|
||||
# additional_animals and birds.
|
||||
>>> birds.isdisjoint(additional_animals)
|
||||
True
|
||||
|
||||
# Animals and mammals have shared elements.
|
||||
# **Note** The first object needs to be a set or converted to a set
|
||||
# since .isdisjoint() is a set method.
|
||||
>>> set(animals).isdisjoint(mammals)
|
||||
False
|
||||
```
|
||||
|
||||
|
||||
### Subsets and Supersets
|
||||
|
||||
`<set>.issubset(<other_collection>)` is used to check if every element in `<set>` is also in `<other_collection>`.
|
||||
The operator form is `<set> <= <other_set>`:
|
||||
|
||||
|
||||
```python
|
||||
# Both mammals and additional_animals are lists.
|
||||
>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
|
||||
>>> additional_animals = ['pangolin', 'panda', 'parrot',
|
||||
'lemur', 'tiger', 'pangolin']
|
||||
|
||||
# Animals is a dict.
|
||||
>>> animals = {'chicken': 'white',
|
||||
'sparrow': 'grey',
|
||||
'eagle': 'brown and white',
|
||||
'albatross': 'grey and white',
|
||||
'crow': 'black',
|
||||
'elephant': 'grey',
|
||||
'dog': 'rust',
|
||||
'cow': 'black and white',
|
||||
'tiger': 'orange and black',
|
||||
'cat': 'grey',
|
||||
'squirrel': 'black'}
|
||||
|
||||
# Birds is a set.
|
||||
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
|
||||
|
||||
# Set methods will take any iterable as an argument.
|
||||
# All members of birds are also members of animals.
|
||||
>>> birds.issubset(animals)
|
||||
True
|
||||
|
||||
# All members of mammals also appear in animals.
|
||||
# **Note** The first object needs to be a set or converted to a set
|
||||
# since .issubset() is a set method.
|
||||
>>> set(mammals).issubset(animals)
|
||||
True
|
||||
|
||||
# Both objects need to be sets to use a set operator
|
||||
>>> birds <= set(mammals)
|
||||
False
|
||||
|
||||
# A set is always a loose subset of itself.
|
||||
>>> set(additional_animals) <= set(additional_animals)
|
||||
True
|
||||
```
|
||||
|
||||
`<set>.issuperset(<other_collection>)` is the inverse of `.issubset()`.
|
||||
It is used to check if every element in `<other_collection>` is also in `<set>`.
|
||||
The operator form is `<set> >= <other_set>`:
|
||||
|
||||
|
||||
```python
|
||||
# All members of mammals also appear in animals.
|
||||
# **Note** The first object needs to be a set or converted to a set
|
||||
# since .issuperset() is a set method.
|
||||
>>> set(animals).issuperset(mammals)
|
||||
True
|
||||
|
||||
# All members of animals do not show up as members of birds.
|
||||
>>> birds.issuperset(animals)
|
||||
False
|
||||
|
||||
# Both objects need to be sets to use a set operator
|
||||
>>> birds >= set(mammals)
|
||||
False
|
||||
|
||||
# A set is always a loose superset of itself.
|
||||
>>> set(animals) >= set(animals)
|
||||
True
|
||||
```
|
||||
|
||||
|
||||
### Set Intersections
|
||||
|
||||
`<set>.intersection(*<other iterables>)` returns a new `set` with elements common to the original `set` and all `<others>` (_in other words, the `set` where everything [intersects][intersection]_).
|
||||
The operator version of this method is `<set> & <other set> & <other set 2> & ... <other set n>`:
|
||||
|
||||
|
||||
```python
|
||||
>>> perennials = {'Annatto','Asafetida','Asparagus','Azalea',
|
||||
'Winter Savory', 'Broccoli','Curry Leaf','Fennel',
|
||||
'Kaffir Lime','Kale','Lavender','Mint','Oranges',
|
||||
'Oregano', 'Tarragon', 'Wild Bergamot'}
|
||||
|
||||
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Marjoram',
|
||||
'Summer Squash', 'Okra','Shallots', 'Basil',
|
||||
'Cilantro', 'Cumin', 'Sunflower', 'Chervil',
|
||||
'Summer Savory'}
|
||||
|
||||
>>> herbs = ['Annatto','Asafetida','Basil','Chervil','Cilantro',
|
||||
'Curry Leaf','Fennel','Kaffir Lime','Lavender',
|
||||
'Marjoram','Mint','Oregano','Summer Savory'
|
||||
'Tarragon','Wild Bergamot','Wild Celery',
|
||||
'Winter Savory']
|
||||
|
||||
|
||||
# Methods will take any iterable as an argument.
|
||||
>>> perennial_herbs = perennials.intersection(herbs)
|
||||
{'Annatto', 'Asafetida', 'Curry Leaf', 'Fennel', 'Kaffir Lime',
|
||||
'Lavender', 'Mint', 'Oregano', 'Wild Bergamot','Winter Savory'}
|
||||
|
||||
# Operators require both groups be sets.
|
||||
>>> annuals & set(herbs)
|
||||
{'Basil', 'Chervil', 'Marjoram', 'Cilantro'}
|
||||
```
|
||||
|
||||
|
||||
### Set Unions
|
||||
|
||||
`<set>.union(*<other iterables>)` returns a new `set` with elements from `<set>` and all `<other iterables>`.
|
||||
The operator form of this method is `<set> | <other set 1> | <other set 2> | ... | <other set n>`:
|
||||
|
||||
|
||||
```python
|
||||
>>> perennials = {'Asparagus', 'Broccoli', 'Sweet Potato', 'Kale'}
|
||||
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Summer Squash'}
|
||||
>>> more_perennials = ['Radicchio', 'Rhubarb',
|
||||
'Spinach', 'Watercress']
|
||||
|
||||
# Methods will take any iterable as an argument.
|
||||
>>> perennials.union(more_perennials)
|
||||
{'Asparagus','Broccoli','Kale','Radicchio','Rhubarb',
|
||||
'Spinach','Sweet Potato','Watercress'}
|
||||
|
||||
# Operators require sets.
|
||||
>>> set(more_perennials) | perennials
|
||||
{'Asparagus',
|
||||
'Broccoli',
|
||||
'Kale',
|
||||
'Radicchio',
|
||||
'Rhubarb',
|
||||
'Spinach',
|
||||
'Sweet Potato',
|
||||
'Watercress'}
|
||||
```
|
||||
|
||||
|
||||
### Set Differences
|
||||
|
||||
`<set>.difference(*<other iterables>)` returns a new `set` with elements from the original `<set>` that are not in `<others>`.
|
||||
The operator version of this method is `<set> - <other set 1> - <other set 2> - ...<other set n>`.
|
||||
|
||||
```python
|
||||
>>> berries_and_veggies = {'Asparagus',
|
||||
'Broccoli',
|
||||
'Watercress',
|
||||
'Goji Berries',
|
||||
'Goose Berries',
|
||||
'Ramps',
|
||||
'Walking Onions',
|
||||
'Blackberries',
|
||||
'Strawberries',
|
||||
'Rhubarb',
|
||||
'Kale',
|
||||
'Artichokes',
|
||||
'Currants'}
|
||||
|
||||
>>> veggies = ('Asparagus', 'Broccoli', 'Watercress', 'Ramps',
|
||||
'Walking Onions', 'Rhubarb', 'Kale', 'Artichokes')
|
||||
|
||||
# Methods will take any iterable as an argument.
|
||||
>>> berries = berries_and_veggies.difference(veggies)
|
||||
{'Blackberries','Currants','Goji Berries',
|
||||
'Goose Berries', 'Strawberries'}
|
||||
|
||||
# Operators require sets.
|
||||
>>> berries_and_veggies - berries
|
||||
{'Artichokes','Asparagus','Broccoli','Kale',
|
||||
'Ramps','Rhubarb','Walking Onions','Watercress'}
|
||||
```
|
||||
|
||||
|
||||
`<set>.symmetric_difference(<other iterable>)` returns a new `set` that contains elements that are in `<set>` OR `<other>`, **but not in both**.
|
||||
The operator version of this method is `<set> ^ <other set>`:
|
||||
|
||||
|
||||
```python
|
||||
>>> plants_1 = {'🌲','🍈','🌵', '🥑','🌴', '🥭'}
|
||||
>>> plants_2 = ('🌸','🌴', '🌺', '🌲', '🌻', '🌵')
|
||||
|
||||
|
||||
# Methods will take any iterable as an argument.
|
||||
>>> fruit_and_flowers = plants_1.symmetric_difference(plants_2)
|
||||
>>> fruit_and_flowers
|
||||
{'🌸', '🌺', '🍈', '🥑', '🥭','🌻' }
|
||||
|
||||
|
||||
# Operators require both groups be sets.
|
||||
>>> fruit_and_flowers ^ plants_1
|
||||
{'🌲', '🌸', '🌴', '🌵','🌺', '🌻'}
|
||||
|
||||
>>> fruit_and_flowers ^ plants_2
|
||||
{ '🥑', '🌴','🌲', '🌵', '🍈', '🥭'}
|
||||
```
|
||||
|
||||
~~~~exercism/note
|
||||
|
||||
A symmetric difference of more than two sets will result in a `set` that includes both the elements unique to each `set` AND elements shared between more than two sets in the series (_details in the Wikipedia article on [symmetric difference][symmetric_difference]_).
|
||||
|
||||
To obtain only items unique to each `set` in the series, intersections between all 2-set combinations need to be aggregated in a separate step, and removed:
|
||||
|
||||
|
||||
```python
|
||||
>>> one = {'black pepper','breadcrumbs','celeriac','chickpea flour',
|
||||
'flour','lemon','parsley','salt','soy sauce',
|
||||
'sunflower oil','water'}
|
||||
|
||||
>>> two = {'black pepper','cornstarch','garlic','ginger',
|
||||
'lemon juice','lemon zest','salt','soy sauce','sugar',
|
||||
'tofu','vegetable oil','vegetable stock','water'}
|
||||
|
||||
>>> three = {'black pepper','garlic','lemon juice','mixed herbs',
|
||||
'nutritional yeast', 'olive oil','salt','silken tofu',
|
||||
'smoked tofu','soy sauce','spaghetti','turmeric'}
|
||||
|
||||
>>> four = {'barley malt','bell pepper','cashews','flour',
|
||||
'fresh basil','garlic','garlic powder', 'honey',
|
||||
'mushrooms','nutritional yeast','olive oil','oregano',
|
||||
'red onion', 'red pepper flakes','rosemary','salt',
|
||||
'sugar','tomatoes','water','yeast'}
|
||||
|
||||
>>> intersections = (one & two | one & three | one & four |
|
||||
two & three | two & four | three & four)
|
||||
...
|
||||
{'black pepper','flour','garlic','lemon juice','nutritional yeast',
|
||||
'olive oil','salt','soy sauce', 'sugar','water'}
|
||||
|
||||
# The ^ operation will include some of the items in intersections,
|
||||
# which means it is not a "clean" symmetric difference - there
|
||||
# are overlapping members.
|
||||
>>> (one ^ two ^ three ^ four) & intersections
|
||||
{'black pepper', 'garlic', 'soy sauce', 'water'}
|
||||
|
||||
# Overlapping members need to be removed in a separate step
|
||||
# when there are more than two sets that need symmetric difference.
|
||||
>>> (one ^ two ^ three ^ four) - intersections
|
||||
...
|
||||
{'barley malt','bell pepper','breadcrumbs', 'cashews','celeriac',
|
||||
'chickpea flour','cornstarch','fresh basil', 'garlic powder',
|
||||
'ginger','honey','lemon','lemon zest','mixed herbs','mushrooms',
|
||||
'oregano','parsley','red onion','red pepper flakes','rosemary',
|
||||
'silken tofu','smoked tofu','spaghetti','sunflower oil', 'tofu',
|
||||
'tomatoes','turmeric','vegetable oil','vegetable stock','yeast'}
|
||||
```
|
||||
|
||||
[symmetric_difference]: https://en.wikipedia.org/wiki/Symmetric_difference
|
||||
~~~~
|
||||
|
||||
[hashable]: https://docs.python.org/3.7/glossary.html#term-hashable
|
||||
[intersection]: https://www.mathgoodies.com/lessons/sets/intersection
|
||||
[mathematical-sets]: https://en.wikipedia.org/wiki/Set_theory#Basic_concepts_and_notation
|
||||
[operator]: https://www.computerhope.com/jargon/o/operator.htm
|
||||
[type-frozenset]: https://docs.python.org/3/library/stdtypes.html#frozenset
|
||||
[type-set]: https://docs.python.org/3/library/stdtypes.html#set
|
||||
|
||||
## Instructions
|
||||
|
||||
You and your business partners operate a small catering company. You've just agreed to run an event for a local cooking club that features "club favorite" dishes. The club is inexperienced in hosting large events, and needs help with organizing, shopping, prepping and serving. You've decided to write some small Python scripts to speed the whole planning process along.
|
||||
|
||||
## 1. Clean up Dish Ingredients
|
||||
|
||||
The event recipes were added from various sources and their ingredients appear to have duplicate (_or more_) entries — you don't want to end up purchasing excess items!
|
||||
Before the shopping and cooking can commence, each dish's ingredient list needs to be "cleaned".
|
||||
|
||||
Implement the `clean_ingredients(<dish_name>, <dish_ingredients>)` function that takes the name of a dish and a `list` of ingredients.
|
||||
This function should return a `tuple` with the name of the dish as the first item, followed by the de-duped `set` of ingredients.
|
||||
|
||||
|
||||
```python
|
||||
>>> clean_ingredients('Punjabi-Style Chole', ['onions', 'tomatoes', 'ginger paste', 'garlic paste', 'ginger paste', 'vegetable oil', 'bay leaves', 'cloves', 'cardamom', 'cilantro', 'peppercorns', 'cumin powder', 'chickpeas', 'coriander powder', 'red chili powder', 'ground turmeric', 'garam masala', 'chickpeas', 'ginger', 'cilantro'])
|
||||
|
||||
>>> ('Punjabi-Style Chole', {'garam masala', 'bay leaves', 'ground turmeric', 'ginger', 'garlic paste', 'peppercorns', 'ginger paste', 'red chili powder', 'cardamom', 'chickpeas', 'cumin powder', 'vegetable oil', 'tomatoes', 'coriander powder', 'onions', 'cilantro', 'cloves'})
|
||||
```
|
||||
|
||||
## 2. Cocktails and Mocktails
|
||||
|
||||
The event is going to include both cocktails and "mocktails" - mixed drinks _without_ the alcohol.
|
||||
You need to ensure that "mocktail" drinks are truly non-alcoholic and the cocktails do indeed _include_ alcohol.
|
||||
|
||||
Implement the `check_drinks(<drink_name>, <drink_ingredients>)` function that takes the name of a drink and a `list` of ingredients.
|
||||
The function should return the name of the drink followed by "Mocktail" if the drink has no alcoholic ingredients, and drink name followed by "Cocktail" if the drink includes alcohol.
|
||||
For the purposes of this exercise, cocktails will only include alcohols from the ALCOHOLS constant in `sets_categories_data.py`:
|
||||
|
||||
```python
|
||||
>>> from sets_categories_data import ALCOHOLS
|
||||
|
||||
>>> check_drinks('Honeydew Cucumber', ['honeydew', 'coconut water', 'mint leaves', 'lime juice', 'salt', 'english cucumber'])
|
||||
...
|
||||
'Honeydew Cucumber Mocktail'
|
||||
|
||||
>>> check_drinks('Shirley Tonic', ['cinnamon stick', 'scotch', 'whole cloves', 'ginger', 'pomegranate juice', 'sugar', 'club soda'])
|
||||
...
|
||||
'Shirley Tonic Cocktail'
|
||||
```
|
||||
|
||||
## 3. Categorize Dishes
|
||||
|
||||
The guest list includes diners with different dietary needs, and your staff will need to separate the dishes into Vegan, Vegetarian, Paleo, Keto, and Omnivore.
|
||||
|
||||
Implement the `categorize_dish(<dish_name>, <dish_ingredients>)` function that takes a dish name and a `set` of that dish's ingredients.
|
||||
The function should return a string with the `dish name: <CATEGORY>` (_which meal category the dish belongs to_).
|
||||
All dishes will "fit" into one of the categories imported from `sets_categories_data.py` (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE).
|
||||
|
||||
```python
|
||||
>>> from sets_categories_data import VEGAN, VEGETARIAN, PALEO, KETO, OMNIVORE
|
||||
|
||||
|
||||
>>> categorize_dish('Sticky Lemon Tofu', {'tofu', 'soy sauce', 'salt', 'black pepper', 'cornstarch', 'vegetable oil', 'garlic', 'ginger', 'water', 'vegetable stock', 'lemon juice', 'lemon zest', 'sugar'})
|
||||
...
|
||||
'Sticky Lemon Tofu: VEGAN'
|
||||
|
||||
>>> categorize_dish('Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', {'shrimp', 'bacon', 'avocado', 'chickpeas', 'fresh tortillas', 'sea salt', 'guajillo chile', 'slivered almonds', 'olive oil', 'butter', 'black pepper', 'garlic', 'onion'})
|
||||
...
|
||||
'Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole: OMNIVORE'
|
||||
```
|
||||
|
||||
## 4. Label Allergens and Restricted Foods
|
||||
|
||||
Some guests have allergies and additional dietary restrictions.
|
||||
These ingredients need to be tagged/annotated for each dish so that they don't cause issues.
|
||||
|
||||
Implement the `tag_special_ingredients(<dish>)` function that takes a `tuple` with the dish name in the first position, and a `list` or `set` of ingredients for that dish in the second position.
|
||||
Return the dish name followed by the `set` of ingredients that require a special note on the dish description.
|
||||
Dish ingredients inside a `list` may or may not have duplicates.
|
||||
For the purposes of this exercise, all allergens or special ingredients that need to be labeled are in the SPECIAL_INGREDIENTS constant imported from `sets_categories_data.py`.
|
||||
|
||||
```python
|
||||
>>> from sets_categories_data import SPECIAL_INGREDIENTS
|
||||
|
||||
>>> tag_special_ingredients(('Ginger Glazed Tofu Cutlets', ['tofu', 'soy sauce', 'ginger', 'corn starch', 'garlic', 'brown sugar', 'sesame seeds', 'lemon juice']))
|
||||
...
|
||||
('Ginger Glazed Tofu Cutlets', {'garlic','soy sauce','tofu'})
|
||||
|
||||
>>> tag_special_ingredients(('Arugula and Roasted Pork Salad', ['pork tenderloin', 'arugula', 'pears', 'blue cheese', 'pine nuts', 'balsamic vinegar', 'onions', 'black pepper']))
|
||||
...
|
||||
('Arugula and Roasted Pork Salad', {'pork tenderloin', 'blue cheese', 'pine nuts', 'onions'})
|
||||
```
|
||||
|
||||
## 5. Compile a "Master List" of Ingredients
|
||||
|
||||
In preparation for ordering and shopping, you'll need to compile a "master list" of ingredients for everything on the menu (_quantities to be filled in later_).
|
||||
|
||||
Implement the `compile_ingredients(<dishes>)` function that takes a `list` of dishes and returns a set of all ingredients in all listed dishes.
|
||||
Each individual dish is represented by its `set` of ingredients.
|
||||
|
||||
```python
|
||||
dishes = [ {'tofu', 'soy sauce', 'ginger', 'corn starch', 'garlic', 'brown sugar', 'sesame seeds', 'lemon juice'},
|
||||
{'pork tenderloin', 'arugula', 'pears', 'blue cheese', 'pine nuts',
|
||||
'balsamic vinegar', 'onions', 'black pepper'},
|
||||
{'honeydew', 'coconut water', 'mint leaves', 'lime juice', 'salt', 'english cucumber'}]
|
||||
|
||||
>>> compile_ingredients(dishes)
|
||||
...
|
||||
{'arugula', 'brown sugar', 'honeydew', 'coconut water', 'english cucumber', 'balsamic vinegar', 'mint leaves', 'pears', 'pork tenderloin', 'ginger', 'blue cheese', 'soy sauce', 'sesame seeds', 'black pepper', 'garlic', 'lime juice', 'corn starch', 'pine nuts', 'lemon juice', 'onions', 'salt', 'tofu'}
|
||||
```
|
||||
|
||||
## 6. Pull out Appetizers for Passing on Trays
|
||||
|
||||
The hosts have given you a list of dishes they'd like prepped as "bite-sized" appetizers to be served on trays.
|
||||
You need to pull these from the main list of dishes being prepared as larger servings.
|
||||
|
||||
Implement the `separate_appetizers(<dishes>, <appetizers>)` function that takes a `list` of dish names and a `list` of appetizer names.
|
||||
The function should return the `list` of dish names with appetizer names removed.
|
||||
Either the `<dishes>` or `<appetizers>` `list` could contain duplicates and may require de-duping.
|
||||
|
||||
```python
|
||||
dishes = ['Avocado Deviled Eggs','Flank Steak with Chimichurri and Asparagus', 'Kingfish Lettuce Cups',
|
||||
'Grilled Flank Steak with Caesar Salad','Vegetarian Khoresh Bademjan','Avocado Deviled Eggs',
|
||||
'Barley Risotto','Kingfish Lettuce Cups']
|
||||
|
||||
appetizers = ['Kingfish Lettuce Cups','Avocado Deviled Eggs','Satay Steak Skewers',
|
||||
'Dahi Puri with Black Chickpeas','Avocado Deviled Eggs','Asparagus Puffs',
|
||||
'Asparagus Puffs']
|
||||
|
||||
>>> separate_appetizers(dishes, appetizers)
|
||||
...
|
||||
['Vegetarian Khoresh Bademjan', 'Barley Risotto', 'Flank Steak with Chimichurri and Asparagus',
|
||||
'Grilled Flank Steak with Caesar Salad']
|
||||
```
|
||||
|
||||
## 7. Find Ingredients Used in Only One Recipe
|
||||
|
||||
Within each category (_Vegan, Vegetarian, Paleo, Keto, Omnivore_), you're going to pull out ingredients that appear in only one dish.
|
||||
These "singleton" ingredients will be assigned a special shopper to ensure they're not forgotten in the rush to get everything else done.
|
||||
|
||||
Implement the `singleton_ingredients(<dishes>, <INTERSECTIONS>)` function that takes a `list` of dishes and a `<CATEGORY>_INTERSECTIONS` constant for the same category.
|
||||
Each dish is represented by a `set` of its ingredients.
|
||||
Each `<CATEGORY>_INTERSECTIONS` is a `set` of ingredients that appear in more than one dish in the category.
|
||||
Using set operations, your function should return a `set` of "singleton" ingredients (_ingredients appearing in only one dish in the category_).
|
||||
|
||||
```python
|
||||
from sets_categories_data import example_dishes, EXAMPLE_INTERSECTION
|
||||
|
||||
>>> singleton_ingredients(example_dishes, EXAMPLE_INTERSECTION)
|
||||
...
|
||||
{'garlic powder', 'sunflower oil', 'mixed herbs', 'cornstarch', 'celeriac', 'honey', 'mushrooms', 'bell pepper', 'rosemary', 'parsley', 'lemon', 'yeast', 'vegetable oil', 'vegetable stock', 'silken tofu', 'tofu', 'cashews', 'lemon zest', 'smoked tofu', 'spaghetti', 'ginger', 'breadcrumbs', 'tomatoes', 'barley malt', 'red pepper flakes', 'oregano', 'red onion', 'fresh basil'}
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @bethanyg
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @zepam
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,121 @@
|
||||
"""Functions for compiling dishes and ingredients for a catering company."""
|
||||
|
||||
from sets_categories_data import (
|
||||
VEGAN,
|
||||
VEGETARIAN,
|
||||
KETO,
|
||||
PALEO,
|
||||
OMNIVORE,
|
||||
ALCOHOLS,
|
||||
SPECIAL_INGREDIENTS,
|
||||
)
|
||||
|
||||
|
||||
def clean_ingredients(dish_name, dish_ingredients):
|
||||
"""Remove duplicates from `dish_ingredients`.
|
||||
|
||||
:param dish_name: str - containing the dish name.
|
||||
:param dish_ingredients: list - dish ingredients.
|
||||
:return: tuple - containing (dish_name, ingredient set).
|
||||
|
||||
This function should return a `tuple` with the name of the dish as the first item,
|
||||
followed by the de-duped `set` of ingredients as the second item.
|
||||
"""
|
||||
return (dish_name, set(dish_ingredients))
|
||||
|
||||
|
||||
def check_drinks(drink_name, drink_ingredients):
|
||||
"""Append "Cocktail" (alcohol) or "Mocktail" (no alcohol) to `drink_name`, based on `drink_ingredients`.
|
||||
|
||||
:param drink_name: str - name of the drink.
|
||||
:param drink_ingredients: list - ingredients in the drink.
|
||||
:return: str - drink_name appended with "Mocktail" or "Cocktail".
|
||||
|
||||
The function should return the name of the drink followed by "Mocktail" (non-alcoholic) and drink
|
||||
name followed by "Cocktail" (includes alcohol).
|
||||
|
||||
"""
|
||||
return drink_name + (
|
||||
" Cocktail" if ALCOHOLS.intersection(
|
||||
drink_ingredients) else " Mocktail"
|
||||
)
|
||||
|
||||
|
||||
def categorize_dish(dish_name, dish_ingredients):
|
||||
"""Categorize `dish_name` based on `dish_ingredients`.
|
||||
|
||||
:param dish_name: str - dish to be categorized.
|
||||
:param dish_ingredients: set - ingredients for the dish.
|
||||
:return: str - the dish name appended with ": <CATEGORY>".
|
||||
|
||||
This function should return a string with the `dish name: <CATEGORY>` (which meal category the dish belongs to).
|
||||
`<CATEGORY>` can be any one of (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE).
|
||||
All dishes will "fit" into one of the categories imported from `sets_categories_data.py`
|
||||
|
||||
"""
|
||||
category: str = ""
|
||||
if dish_ingredients <= VEGAN:
|
||||
category = ": VEGAN"
|
||||
if dish_ingredients <= VEGETARIAN:
|
||||
category = ": VEGETARIAN"
|
||||
if dish_ingredients <= PALEO:
|
||||
category = ": PALEO"
|
||||
if dish_ingredients <= KETO:
|
||||
category = ": KETO"
|
||||
if dish_ingredients <= OMNIVORE:
|
||||
category = ": OMNIVORE"
|
||||
return dish_name + category
|
||||
|
||||
|
||||
def tag_special_ingredients(dish):
|
||||
"""Compare `dish` ingredients to `SPECIAL_INGREDIENTS`.
|
||||
|
||||
:param dish: tuple - of (dish name, list of dish ingredients).
|
||||
:return: tuple - containing (dish name, dish special ingredients).
|
||||
|
||||
Return the dish name followed by the `set` of ingredients that require a special note on the dish description.
|
||||
For the purposes of this exercise, all allergens or special ingredients that need to be tracked are in the
|
||||
SPECIAL_INGREDIENTS constant imported from `sets_categories_data.py`.
|
||||
"""
|
||||
return (dish[0], set(dish[1]).intersection(SPECIAL_INGREDIENTS))
|
||||
|
||||
|
||||
def compile_ingredients(dishes):
|
||||
"""Create a master list of ingredients.
|
||||
|
||||
:param dishes: list - of dish ingredient sets.
|
||||
:return: set - of ingredients compiled from `dishes`.
|
||||
|
||||
This function should return a `set` of all ingredients from all listed dishes.
|
||||
"""
|
||||
return set().union(*dishes)
|
||||
|
||||
|
||||
def separate_appetizers(dishes, appetizers):
|
||||
"""Determine which `dishes` are designated `appetizers` and remove them.
|
||||
|
||||
:param dishes: list - of dish names.
|
||||
:param appetizers: list - of appetizer names.
|
||||
:return: list - of dish names that do not appear on appetizer list.
|
||||
|
||||
The function should return the list of dish names with appetizer names removed.
|
||||
Either list could contain duplicates and may require de-duping.
|
||||
"""
|
||||
return list(set(dishes).difference(set(appetizers)))
|
||||
|
||||
|
||||
def singleton_ingredients(dishes, intersection):
|
||||
"""Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes).
|
||||
|
||||
:param dishes: list - of ingredient sets.
|
||||
:param intersection: constant - can be one of `<CATEGORY>_INTERSECTIONS` constants imported from `sets_categories_data.py`.
|
||||
:return: set - containing singleton ingredients.
|
||||
|
||||
Each dish is represented by a `set` of its ingredients.
|
||||
|
||||
Each `<CATEGORY>_INTERSECTIONS` is an `intersection` of all dishes in the category. `<CATEGORY>` can be any one of:
|
||||
(VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE).
|
||||
|
||||
The function should return a `set` of ingredients that only appear in a single dish.
|
||||
"""
|
||||
return set().union(*dishes).difference(intersection)
|
||||
@@ -0,0 +1,212 @@
|
||||
# pylint: disable-all
|
||||
# flake8: noqa,
|
||||
VEGAN = {
|
||||
'chives', 'nutritional yeast', 'tomato', 'orange zest', 'pareve puff pastry', 'cashews', 'tofu',
|
||||
'rice vinegar', 'black pepper', 'cardamom powder', 'mustard seeds', 'parev shortcrust pastry',
|
||||
'scallions', 'water', 'chinese eggplants', 'lemon juice', 'smoked paprika', 'cloves', 'basmati rice',
|
||||
'cayenne pepper', 'green onions', 'sunflower oil', 'mixed herbs', 'garlic paste', 'parsley',
|
||||
'fresh red chili', 'flour', 'garlic', 'oregano', 'green beans', 'harissa', 'brandy', 'fresh basil',
|
||||
'coriander', 'vinegar', 'thyme', 'coriander seeds', 'clove powder', 'pomegranate seeds',
|
||||
'sugar', 'yukon gold potato', 'sesame oil', 'cinnamon powder', 'butternut squash', 'allspice powder',
|
||||
'red pepper flakes', 'soy sauce', 'sesame seeds', 'cornstarch', 'mango powder', 'vegetable stock',
|
||||
'raisins', 'barley malt', 'olive oil', 'ground almonds', 'white rice', 'garlic powder', 'walnuts',
|
||||
'saffron powder', 'red chili powder', 'turmeric powder', 'spring onions', 'yeast', 'khmeli suneli',
|
||||
'peanuts', 'bulgur', 'cilantro', 'onion', 'calabash nutmeg', 'black-eyed peas', 'grains of selim',
|
||||
'zucchini', 'currants', 'spaghetti', 'figs', 'red bell pepper', 'lemon zest', 'ground turmeric',
|
||||
'chili flakes', 'chickpea flour', 'hing', 'slivered almonds', 'vegetable oil', 'serrano chili',
|
||||
'salt', 'yellow onions', 'salt', 'coriander powder', 'orange zest', 'garam masala', 'yellow onion',
|
||||
'smoked tofu', 'bell pepper', 'apples', 'brown sugar', 'coconut oil', 'orange juice',
|
||||
'sorghum stems', 'dried blueberries', 'tomato paste', 'curry leaves', 'vegetarian worcestershire sauce',
|
||||
'hot water', 'fresh ginger', 'firm tofu', 'eggplants', 'bell pepper', 'siracha', 'carrot', 'nigella seeds',
|
||||
'vegan butter', "za'atar", 'baking soda', 'brown sugar', 'dried cranberries', 'kosher salt', 'mangoes',
|
||||
'vegan unsweetened yoghurt', 'black peppercorn', 'vinegar', 'dill', 'barberries', 'honey', 'tomatoes',
|
||||
'yellow split peas', 'persian cucumber', 'turmeric', 'lemon', 'cumin', 'oil', 'mushrooms', 'spring onion',
|
||||
'pomegranate concentrate', 'cumin seeds', 'balsamic vinegar', 'ripe plantains', 'celeriac', 'breadcrumbs',
|
||||
'ginger', 'dried cherries', 'red onion', 'rosemary', 'chopped parsley', 'corn', 'cumin powder', 'pecans',
|
||||
'silken tofu', 'pomegranate molasses', 'carrot', 'corn flour', 'mashed potatoes'
|
||||
}
|
||||
|
||||
|
||||
VEGETARIAN = {
|
||||
'almonds', 'chives', 'limes', 'puff pastry', 'onion', 'cashews', 'red cabbage', 'red wine vinegar',
|
||||
'brussel sprouts', 'fresh corn', 'black pepper', 'lemon juice', 'roasted corn', 'eggs',
|
||||
'fresh cilantro leaves', 'shiitake mushrooms', 'sunflower oil', 'sage', 'dijon mustard',
|
||||
'blanched almonds', 'dates', 'flour', 'fresh pea tendrils', 'garlic', 'egg', 'green beans',
|
||||
'yukon gold potato', 'vermicelli noodles', 'onions', 'avocado', 'dried lasagna noodles',
|
||||
'thyme', 'cauliflower', 'basil', 'watercress', 'black beans', 'butternut squash', 'red thai chili',
|
||||
'masa', 'red chili', 'red onions', 'jalapeño chili', 'grated nutmeg', 'feta cheese', 'hazelnuts',
|
||||
'soy sauce', 'shallots', 'chipotle chili', 'vegetable bullion', 'fresh cherry tomatoes', 'olive oil',
|
||||
'milk', 'fresh cherry bocconcini', 'crema', 'marmite', 'walnuts', 'nutmeg', 'ricotta cheese',
|
||||
'chestnuts', 'mint leaves', 'lime juice', 'white wine', 'apples', 'pearl barley', 'cotija cheese',
|
||||
'zucchini', 'currants', 'leek', 'pomegranate', 'lemon zest', 'avocados', 'parmesan cheese', 'mint',
|
||||
'leeks', 'fresh artichoke hearts', 'vegetable oil', 'brazil nuts', 'red chili', 'sharp white cheddar',
|
||||
'salt', 'pepitas', 'green lentils', 'beets', 'celery', 'smoked tofu', 'fresh tomatoes',
|
||||
'puff pastry sheets', 'palm sugar', 'vegetarian fish sauce', 'oil marinated artichokes', 'hot water',
|
||||
'chickpeas', 'firm tofu', 'wombok', 'carrot', 'asparagus', 'bean sprouts', 'kosher salt',
|
||||
'pasilla chili', 'tomatillos', 'parmesan rind', 'pasta sheets', 'cream', 'butter', 'croutons',
|
||||
'lacinato kale', 'fresh or frozen fava beans', 'fresh pumpkin', 'honey', 'tomatoes', 'olives',
|
||||
'capers', 'pine nuts', 'lemon', 'cumin', 'ancho chili', 'fresh peas', 'spring roll wrappers',
|
||||
'balsamic vinegar', 'portobello mushrooms', 'breadcrumbs', 'blue cheese', 'red onion',
|
||||
'rosemary', 'pecans', 'carrot', 'corn flour', 'toasted peanuts'
|
||||
}
|
||||
|
||||
PALEO = {
|
||||
'cinnamon', 'chiles de árbol', 'chives', 'limes', 'allspice', 'zucchini', 'seranno chili', 'lemon zest',
|
||||
'apple cider vinegar', 'avocados', 'cashews', 'mango', 'cilantro leaves', 'pepitas', 'white chicken',
|
||||
'chipotles', 'black pepper', 'scallions', 'pumpkin puree', 'water', 'serrano chili', 'lemon juice',
|
||||
'smoked paprika', 'homemade apricot honey preserves', 'eggs', 'salt', 'flank steak', 'fresh cilantro leaves',
|
||||
'cider vinegar', 'cloves', 'purple sweet potato', 'coconut yogurt', 'green onions', 'tilapia',
|
||||
'yellow bell pepper', 'coconut oil', 'whole chicken', 'coconut oil', 'safflower oil', 'roma tomatoes',
|
||||
'fresh red chili', 'fresh thai chili', 'shrimp', 'garlic', 'onions', 'lime', 'avocado', 'fresh parsley',
|
||||
'cauliflower', 'shredded red cabbage', 'basil', 'baking soda', 'serrano chili',
|
||||
'cherry tomatoes', 'kale', 'bacon', 'kosher salt', 'mangoes', 'lacinato kale', 'shallots', 'pineapple',
|
||||
'chipotle chili', 'white vinegar', 'honey', 'tomatoes', 'homemade tamarind concentrate',
|
||||
'mexican oregano', 'olive oil', 'pine nuts', 'garlic powder', 'coconut flour', 'green bell pepper',
|
||||
'dried apricots', 'cumin', 'nutmeg', 'kosher salt', 'onions', 'mustard seed', 'lemons', 'lime zest',
|
||||
'ground cumin', 'almond butter', 'chili powder', 'lime juice', 'paleo mayonnaise', 'pork chops',
|
||||
'cilantro', 'onion', 'red bell pepper', 'paleo parmesan cheese', 'radishes', 'avocado oil',
|
||||
'dijon mustard', 'avocado mayonnaise', 'castelfranco radicchio', 'worcestershire sauce', 'treviso'
|
||||
}
|
||||
|
||||
KETO = {
|
||||
'cinnamon', 'avocado oil', 'chives', 'sriacha', 'almond flour', 'crunchy peanut butter',
|
||||
'cucumbers', 'cream cheese', 'red cabbage', 'red wine vinegar', 'brussel sprouts', 'black pepper',
|
||||
'cardamom powder', 'mustard seeds', 'scallions', 'kecap manis', 'lemon juice', 'eggs', 'tahini',
|
||||
'cloves', 'green onions', 'dijon mustard', 'garlic paste', 'watermelon radishes', 'parmesan',
|
||||
'parsley', 'star anise', 'fresh cucumber', 'fresh red chili', 'shrimp', 'garlic', 'oregano',
|
||||
'fennel bulb', 'harissa', 'dutch carrot', 'fresh basil', 'avocado', 'clove powder', 'coriander seeds',
|
||||
'thyme', 'fresh parsley', 'chicken', 'cauliflower', 'basil', 'watercress', 'cinnamon powder',
|
||||
'cherry tomatoes', 'soy sauce', 'sesame seeds', 'micro cilantro', 'mozzarella cheese', 'shallots',
|
||||
'mango powder', 'chipotle chili', 'olive oil', 'spinach', 'pink peppercorns', 'coconut flour',
|
||||
'salmon steaks', 'dark soy sauce', 'red chili powder', 'turmeric powder', 'spring onions',
|
||||
'lime juice', 'ginger garlic paste', 'pork chops', 'peanuts', 'dried fenugreek leaves', 'cilantro',
|
||||
'onion', 'salmon fillets', 'toasted buckwheat', 'whole small crimini mushrooms', 'caster sugar',
|
||||
'granny smith apples', 'green cabbage', 'apple cider vinegar', 'chili flakes', 'parmesan cheese',
|
||||
'hing', 'castelfranco radicchio', 'cilantro leaves', 'fresh greek yogurt', 'roasted chicken', 'ghee',
|
||||
'flaxmeal', 'flank steak', 'salt', 'coriander powder', 'boned chicken', 'red chili flakes',
|
||||
'garam masala', 'almond meal', 'peanut oil', 'tomato paste', 'oyster sauce',
|
||||
'curry leaves', 'fresh ginger', 'cardamom', 'radishes', 'little gem lettuce heads',
|
||||
'grilled king fish', 'carrot', 'cinnamon sticks', 'heavy cream', 'asparagus', 'nigella seeds',
|
||||
'light soy sauce', 'pork belly', 'green chili', 'mangoes', 'red and green thai chili', 'butter',
|
||||
'vinegar', 'dill', 'fish sauce', 'white vinegar', 'tomatoes', 'mirin',
|
||||
'avocado mayonnaise', 'turmeric', 'lemon', 'cumin', 'fennel seeds', 'lemon juice', 'salt',
|
||||
'roasted peanuts', 'ginger', 'red onion', 'rosemary', 'cumin powder', 'cashew nuts', 'pecans',
|
||||
'green chili','whole small crimini mushrooms', 'monk fruit', 'sour cream'
|
||||
}
|
||||
|
||||
OMNIVORE = {
|
||||
'clams', 'prawns', 'white wine vinegar', 'date syrup', 'limes', 'tomato', 'coriander',
|
||||
'black chickpeas', 'yellow bell pepper', 'black cardamom', 'baby squid', 'pepitas',
|
||||
'red cabbage', 'baby scallops', 'green cardamom', 'black pepper', 'chaat masala', 'water',
|
||||
'lemon juice', 'tahini', 'cloves', 'white pepper', 'fennel bulbs', 'tomato puree',
|
||||
'maggi cubes', 'couscous', 'yellow mustard', 'parsley', 'sriracha', 'roma tomatoes',
|
||||
'shrimp', 'garlic', 'oregano', 'chicken wings', 'yukon gold potato', 'harissa', 'onions',
|
||||
'avocado', 'thyme', 'chicken', 'sugar', 'flat-leaf parsley', 'celery seeds', 'cherry tomatoes',
|
||||
'mayonnaise', 'scallion chutney', 'red pepper flakes', 'hazelnuts', 'soy sauce', 'sesame seeds',
|
||||
'red snapper', 'white onion', 'vegetable bullion', 'marjoram', 'pani puri', 'olive oil', 'rice',
|
||||
'serrano chili', 'tamarind concentrate', 'lime juice', 'white wine', 'beef brisket', 'cilantro',
|
||||
'onion', 'crushed red pepper flakes', 'chiles de árbol', 'fresh mint', 'zucchini', 'red bell pepper',
|
||||
'yoghurt', 'apple cider vinegar', 'parmesan cheese', 'slivered almonds', 'whole-milk yogurt',
|
||||
'anchovy fillets', 'fresh ricotta', 'mint', 'chile manzano', 'roasted chicken', 'sea salt',
|
||||
'fresh thyme', 'vegetable oil', 'salt', 'mexican crema', 'celery', 'yellow onion',
|
||||
'worcestershire sauce', 'fresh tortillas', 'tomato paste', 'oranges', 'chickpeas',
|
||||
'scotch bonnet pepper', 'shelled large shrimp', 'mussels', 'summer squash', 'salsa',
|
||||
'garlic cloves', 'fish stock', 'bell pepper', 'green bell pepper', 'carrot', 'cinnamon sticks',
|
||||
'thin sev', 'brown sugar', 'baby carrot', 'bacon', 'kosher salt', 'bay leaves', 'anaheim chili',
|
||||
'oaxaca cheese', 'butter', 'vinegar', 'crab legs', 'white vinegar', 'honey', 'tomatoes',
|
||||
'green cabbage', 'toasted bread', 'turmeric', 'lemon', 'cumin', 'black peppercorns', 'poblano chili',
|
||||
'arborio risotto rice', 'fresh corn tortillas', 'balsamic vinegar', 'rhubarb', 'ginger',
|
||||
'guajillo chile', 'filo pastry', 'leg of lamb', 'red onion', 'chipotle adobo sauce', 'rosemary',
|
||||
'chili powder', 'beer', 'carrot'
|
||||
}
|
||||
|
||||
SPECIAL_INGREDIENTS = {'cream','bacon', 'garlic', 'baby scallops', 'mussels', 'baby squid', 'cashews', 'salmon fillets',
|
||||
'filo pastry', 'almonds', 'milk', 'blue cheese', 'clams', 'shrimp', 'tomato puree', 'chocolate',
|
||||
'honey', 'anchovy fillets', 'bulgur', 'prawns', 'parmesan cheese', 'fish', 'shelled large shrimp',
|
||||
'gluten', 'crab legs', 'feta cheese', 'whole-milk yogurt', 'crema', 'firm tofu', 'fish stock',
|
||||
'fresh ricotta', 'tomato paste', 'fresh cherry tomatoes', 'pork chops', 'eggs', 'greek yogurt',
|
||||
'hazelnuts', 'pecans', 'brie cheese', 'oaxaca cheese', 'yellow onion', 'whey', 'silken tofu',
|
||||
'toasted bread', 'parmesan', 'beef', 'tofu', 'flour', 'tomatoes', 'red onion', 'slivered almonds',
|
||||
'strawberries', 'onions', 'pine nuts', 'cherry tomatoes', 'soy sauce', 'oyster sauce',
|
||||
'mozzarella cheese', 'roma tomatoes', 'heavy cream', 'paneer', 'pork tenderloin', 'garlic cloves',
|
||||
'swiss cheese', 'grilled king fish', 'ground almonds', 'tilapia', 'sprint onion', 'couscous',
|
||||
'walnuts', 'semolina', 'yogurt', 'cotija cheese', 'oysters', 'spaghetti', 'cheddar cheese',
|
||||
'butter', 'lobster', 'smoked tofu', 'peanuts', 'ground pork', 'fresh cherry bocconcini',
|
||||
'pork belly', 'toasted peanuts', 'roasted peanuts'
|
||||
}
|
||||
|
||||
ALCOHOLS = {"whiskey", "whisky", "white rum", "dark rum", "bourbon", "rye", "scotch", "vodka",
|
||||
"tequila", "gin", "dry vermouth", "sweet vermouth", "prosecco","aperol", "brandy", "mezcal",
|
||||
"triple sec", "coffee liqueur", "almond liqueur", "champagne", "orange curacao", "rum"
|
||||
}
|
||||
|
||||
|
||||
VEGAN_INTERSECTIONS = {'brown sugar', 'carrot', 'sugar', 'vegetable stock', 'fresh ginger', 'nutritional yeast',
|
||||
'cayenne pepper', 'olive oil', 'lemon', 'ginger', 'red onion', 'pomegranate molasses',
|
||||
'onion', 'water', 'chickpea flour', 'orange zest', 'coconut oil', 'smoked paprika',
|
||||
'lemon zest', 'sunflower oil', 'orange juice', 'black pepper', 'cinnamon powder',
|
||||
'mushrooms', 'cloves', 'salt', 'oil', 'vegan butter', 'turmeric', 'tomato paste',
|
||||
'mustard seeds', 'bell pepper', 'rosemary', 'vinegar', 'tomatoes', 'flour', 'soy sauce',
|
||||
'lemon juice', 'garlic'}
|
||||
|
||||
VEGETARIAN_INTERSECTIONS = {'carrot', 'milk', 'basil', 'green lentils', 'vegetable bullion', 'red onions',
|
||||
'balsamic vinegar', 'lemon', 'olive oil', 'butter', 'honey', 'red chili',
|
||||
'red onion', 'breadcrumbs', 'lemon zest', 'pepitas', 'black pepper', 'fresh peas',
|
||||
'salt', 'firm tofu', 'ricotta cheese', 'kosher salt', 'watercress', 'cream',
|
||||
'parmesan cheese', 'shallots', 'rosemary', 'sage', 'tomatoes', 'walnuts',
|
||||
'lemon juice', 'thyme', 'garlic', 'eggs', 'red wine vinegar'}
|
||||
|
||||
PALEO_INTERSECTIONS = {'basil', 'olive oil', 'honey', 'pine nuts', 'baking soda', 'shrimp', 'cherry tomatoes',
|
||||
'coconut oil', 'cinnamon', 'lemon zest', 'cumin', 'black pepper', 'lime', 'salt',
|
||||
'zucchini', 'kosher salt', 'chipotle chili', 'eggs', 'coconut flour', 'avocado',
|
||||
'cauliflower', 'serrano chili', 'safflower oil', 'tomatoes', 'lemon juice', 'onions',
|
||||
'garlic'}
|
||||
|
||||
KETO_INTERSECTIONS = {'fresh cucumber', 'red cabbage', 'olive oil', 'ginger', 'butter', 'dill', 'red onion',
|
||||
'monk fruit', 'cherry tomatoes', 'spring onions', 'lime juice', 'fish sauce',
|
||||
'sesame seeds', 'black pepper', 'salt', 'chives', 'asparagus', 'eggs',
|
||||
'avocado mayonnaise', 'rosemary', 'cauliflower', 'flank steak', 'lemon juice',
|
||||
'garlic'}
|
||||
|
||||
OMNIVORE_INTERSECTIONS = {'mint', 'carrot', 'fresh mint', 'olive oil', 'lemon', 'ginger', 'butter', 'honey',
|
||||
'leg of lamb', 'red onion', 'bay leaves', 'tamarind concentrate',
|
||||
'worcestershire sauce', 'onion', 'lime juice', 'water', 'anchovy fillets', 'celery',
|
||||
'black pepper', 'cilantro', 'chili powder', 'salt', 'mayonnaise', 'garlic cloves',
|
||||
'kosher salt', 'white onion', 'turmeric', 'rosemary', 'vinegar', 'tomatoes',
|
||||
'sea salt', 'soy sauce', 'lemon juice', 'onions', 'thyme', 'garlic', 'avocado',
|
||||
'fresh corn tortillas', 'tomato paste'}
|
||||
|
||||
|
||||
EXAMPLE_INTERSECTION = {'fresh red chili', 'sugar', 'nutritional yeast', 'fresh ginger', 'red chili powder', 'garlic',
|
||||
'olive oil', 'mashed potatoes', 'garam masala', 'clove powder', 'cumin powder', 'onion',
|
||||
'chickpea flour', 'water', 'turmeric powder', 'hing', 'black pepper', 'cinnamon powder',
|
||||
'cilantro', 'salt', 'oil', 'cardamom powder', 'turmeric', 'garlic paste', 'mustard seeds',
|
||||
'vinegar', 'mangoes', 'nigella seeds', 'serrano chili', 'flour', 'soy sauce', 'coriander seeds',
|
||||
'coriander powder', 'lemon juice', 'mango powder', 'curry leaves'}
|
||||
|
||||
example_dishes = [
|
||||
{'salt', 'breadcrumbs', 'water', 'flour', 'celeriac', 'chickpea flour', 'soy sauce', 'parsley',
|
||||
'sunflower oil', 'lemon', 'black pepper'},
|
||||
|
||||
{'cornstarch', 'salt', 'vegetable oil', 'sugar', 'vegetable stock', 'water', 'tofu', 'soy sauce',
|
||||
'lemon zest', 'lemon juice', 'black pepper', 'ginger', 'garlic'},
|
||||
|
||||
{'salt', 'mixed herbs', 'silken tofu', 'smoked tofu', 'nutritional yeast', 'turmeric', 'soy sauce',
|
||||
'garlic', 'lemon juice', 'olive oil', 'black pepper', 'spaghetti'},
|
||||
|
||||
{'salt', 'mushrooms', 'sugar', 'barley malt', 'nutritional yeast', 'fresh basil', 'olive oil',
|
||||
'honey', 'yeast', 'red onion', 'bell pepper', 'cashews', 'oregano', 'rosemary', 'garlic powder',
|
||||
'tomatoes', 'water', 'flour', 'red pepper flakes', 'garlic'},
|
||||
|
||||
{'mango powder', 'oil', 'salt', 'cardamom powder', 'fresh red chili', 'sugar', 'fresh ginger',
|
||||
'turmeric', 'red chili powder', 'curry leaves', 'garlic paste', 'mustard seeds', 'vinegar',
|
||||
'mashed potatoes', 'garam masala', 'mangoes', 'nigella seeds', 'clove powder', 'serrano chili',
|
||||
'cumin powder', 'onion', 'water', 'chickpea flour', 'coriander seeds', 'turmeric powder', 'hing',
|
||||
'coriander powder', 'cinnamon powder', 'cilantro', 'garlic'},
|
||||
|
||||
{'mango powder', 'oil', 'salt', 'cardamom powder', 'fresh red chili', 'sugar', 'fresh ginger',
|
||||
'turmeric', 'red chili powder', 'curry leaves', 'garlic paste', 'mustard seeds', 'vinegar',
|
||||
'mashed potatoes', 'garam masala', 'mangoes', 'nigella seeds', 'clove powder', 'serrano chili',
|
||||
'cumin powder', 'onion', 'water', 'chickpea flour', 'coriander seeds', 'turmeric powder', 'hing',
|
||||
'coriander powder', 'cinnamon powder', 'cilantro', 'garlic'}
|
||||
]
|
||||
@@ -0,0 +1,119 @@
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
# pylint: disable=deprecated-module
|
||||
from sets import (clean_ingredients,
|
||||
check_drinks,
|
||||
categorize_dish,
|
||||
tag_special_ingredients,
|
||||
compile_ingredients,
|
||||
separate_appetizers,
|
||||
singleton_ingredients)
|
||||
|
||||
|
||||
from sets_categories_data import (VEGAN,
|
||||
VEGETARIAN,
|
||||
KETO,
|
||||
PALEO,
|
||||
OMNIVORE,
|
||||
ALCOHOLS,
|
||||
SPECIAL_INGREDIENTS,
|
||||
VEGAN_INTERSECTIONS,
|
||||
VEGETARIAN_INTERSECTIONS,
|
||||
PALEO_INTERSECTIONS,
|
||||
KETO_INTERSECTIONS,
|
||||
OMNIVORE_INTERSECTIONS)
|
||||
|
||||
from sets_test_data import (recipes_with_duplicates,
|
||||
recipes_without_duplicates,
|
||||
all_drinks,
|
||||
drink_names,
|
||||
dishes_categorized,
|
||||
dishes_to_special_label,
|
||||
dishes_labeled,
|
||||
ingredients_only,
|
||||
dishes_and_appetizers,
|
||||
dishes_cleaned,
|
||||
dishes_and_overlap,
|
||||
singletons)
|
||||
|
||||
|
||||
class SetsTest(unittest.TestCase):
|
||||
|
||||
@pytest.mark.task(taskno=1)
|
||||
def test_clean_ingredients(self):
|
||||
test_data = zip(recipes_with_duplicates[::3], recipes_without_duplicates[::3])
|
||||
|
||||
for variant, (item, result) in enumerate(test_data, start=1):
|
||||
with self.subTest(f"variation #{variant}", inputs="recipes with duplicated ingredients",
|
||||
result="recipe ingredients de-duped"):
|
||||
|
||||
error_msg = (f"Expected the ingredient list for {item[0]} to be de-duplicated, "
|
||||
"but the ingredients were not cleaned as expected.")
|
||||
|
||||
self.assertEqual(clean_ingredients(item[0], item[1]), (result[1], result[2]), msg=error_msg)
|
||||
|
||||
@pytest.mark.task(taskno=2)
|
||||
def test_check_drinks(self):
|
||||
test_data = zip(all_drinks[::2], drink_names[::2])
|
||||
|
||||
for variant, (item, result) in enumerate(test_data, start=1):
|
||||
with self.subTest(f"variation #{variant}", iputs="all drinks", results="drinks classified"):
|
||||
|
||||
error_msg = f"Expected {result} for {item}, but got something else instead."
|
||||
self.assertEqual(check_drinks(item[0], item[1]), (result), msg=error_msg)
|
||||
|
||||
@pytest.mark.task(taskno=3)
|
||||
def test_categorize_dish(self):
|
||||
test_data = zip(sorted(recipes_without_duplicates, reverse=True)[::3], dishes_categorized[::3])
|
||||
|
||||
for variant, (item, result) in enumerate(test_data, start=1):
|
||||
with self.subTest(f"variation #{variant}", inputs="all recipes list", results="categorized dishes"):
|
||||
|
||||
error_message = f"Expected category {result} for {item[0]}, but got a different category instead."
|
||||
self.assertEqual(categorize_dish(item[1], item[2]), (result), msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=4)
|
||||
def test_tag_special_ingredients(self):
|
||||
test_data = zip(dishes_to_special_label[::3], dishes_labeled[::3])
|
||||
|
||||
for variant, (item, result) in enumerate(test_data, start=1):
|
||||
with self.subTest(f"variation #{variant}", inputs="all recipes list", results="special ingredients tagged"):
|
||||
|
||||
error_message = f"Expected {result} for {item}, but got something else instead."
|
||||
self.assertEqual(tag_special_ingredients(item), (result), msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=5)
|
||||
def test_compile_ingredients(self):
|
||||
test_data = zip(ingredients_only, [VEGAN, VEGETARIAN, PALEO, KETO, OMNIVORE])
|
||||
|
||||
for variant, (item, result) in enumerate(test_data, start=1):
|
||||
with self.subTest(f"variation #{variant}", inputs="all ingredients for all recipes",
|
||||
result="combined list of ingredients for all dishes"):
|
||||
|
||||
error_message = "Expected a proper set of combined ingredients, but something went wrong."
|
||||
self.assertEqual(compile_ingredients(item), (result), msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=6)
|
||||
def test_separate_appetizers(self):
|
||||
test_data = zip(dishes_and_appetizers, dishes_cleaned)
|
||||
|
||||
for variant, (item, result) in enumerate(test_data, start=1):
|
||||
with self.subTest(f"variation #{variant}", inputs="dishes with appetizers", results="appetizers only"):
|
||||
|
||||
error_message = "Expected only appetizers returned, but some dishes remain in the group."
|
||||
result_type_error = f"You returned {type(separate_appetizers(item[0], item[1]))}, but a list was expected."
|
||||
self.assertIsInstance(separate_appetizers(item[0], item[1]), list, msg=result_type_error)
|
||||
self.assertEqual(sorted(separate_appetizers(item[0], item[1])), (sorted(result)), msg=error_message)
|
||||
|
||||
@pytest.mark.task(taskno=7)
|
||||
def test_singleton_ingredients(self):
|
||||
test_data = zip(dishes_and_overlap, singletons)
|
||||
|
||||
for variant, (item, result) in enumerate(test_data, start=1):
|
||||
with self.subTest(f"variation #{variant}", inputs="overlapping ingredients",
|
||||
results="ingredients in only one dish"):
|
||||
|
||||
error_message = ("Expected only ingredients that belong to exactly "
|
||||
"one dish, but got multi-dish ingredients instead.")
|
||||
self.assertEqual(singleton_ingredients(item[0], item[1]), (result), msg=error_message)
|
||||
@@ -0,0 +1,459 @@
|
||||
# pylint: disable-all
|
||||
# flake8: noqa,
|
||||
from sets_categories_data import (VEGAN_INTERSECTIONS,
|
||||
VEGETARIAN_INTERSECTIONS,
|
||||
PALEO_INTERSECTIONS,
|
||||
KETO_INTERSECTIONS,
|
||||
OMNIVORE_INTERSECTIONS)
|
||||
|
||||
|
||||
###################################
|
||||
# Data for test_clean_ingredients #
|
||||
###################################
|
||||
|
||||
recipes_with_duplicates = [('Kisir with Warm Pita', ['bulgur', 'pomegranate molasses', 'chopped parsley', 'lemon juice', 'tomato', 'persian cucumber', 'tomato paste', 'spring onion', 'water', 'olive oil', 'bulgur', 'bulgur', 'pomegranate molasses', 'pomegranate molasses', 'pomegranate molasses', 'chopped parsley', 'lemon juice', 'tomato', 'tomato', 'persian cucumber', 'tomato paste', 'tomato paste', 'tomato paste']),
|
||||
('Shakshuka', ['vegan unsweetened yoghurt', 'yellow onion', 'firm tofu', 'smoked paprika', 'tomatoes', 'tomato paste', 'sugar', 'cloves', 'cumin', "za'atar", 'olive oil', 'harissa', 'red bell pepper']),
|
||||
('Vegetarian Khoresh Bademjan', ['yellow split peas', 'tomato paste', 'black pepper', 'pomegranate concentrate', 'yellow onions', 'slivered almonds', 'ground turmeric', 'barberries', 'basmati rice', 'lemon juice', 'hot water', 'cayenne pepper', 'chinese eggplants', 'salt', 'orange juice', 'saffron powder', 'vegan butter', 'orange zest', 'kosher salt', 'yellow split peas', 'yellow split peas', 'tomato paste', 'tomato paste', 'tomato paste', 'black pepper']),
|
||||
('Baked Kelewele', ['smoked paprika', 'black peppercorn', 'red onion', 'grains of selim', 'cayenne pepper', 'calabash nutmeg', 'coconut oil', 'cloves', 'fresh ginger', 'salt', 'ripe plantains', 'smoked paprika', 'black peppercorn', 'black peppercorn', 'red onion', 'grains of selim', 'grains of selim', 'grains of selim']),
|
||||
('Waakye', ['baking soda', 'sorghum stems', 'coconut oil', 'black-eyed peas', 'water', 'salt', 'white rice', 'baking soda', 'baking soda', 'sorghum stems', 'sorghum stems', 'sorghum stems', 'coconut oil']),
|
||||
('Georgian Eggplant Rolls with Walnuts', ['pomegranate seeds', 'oil', 'coriander', 'garlic', 'khmeli suneli', 'eggplants', 'black pepper', 'vinegar', 'walnuts', 'water', 'salt']),
|
||||
('Burmese Tofu with Garlic, Ginger and Chili Sauce', ['soy sauce', 'oil', 'chili flakes', 'garlic', 'brown sugar', 'ginger', 'peanuts', 'rice vinegar', 'spring onions', 'water', 'turmeric', 'salt', 'chickpea flour', 'soy sauce', 'soy sauce', 'oil', 'oil', 'oil', 'chili flakes', 'garlic', 'brown sugar', 'brown sugar', 'ginger', 'peanuts', 'peanuts', 'peanuts']),
|
||||
('Celeriac Schnitzel', ['soy sauce', 'parsley', 'lemon', 'sunflower oil', 'black pepper', 'celeriac', 'breadcrumbs', 'water', 'salt', 'flour', 'chickpea flour']),
|
||||
('Sticky Lemon Tofu', ['soy sauce', 'vegetable stock', 'tofu', 'cornstarch', 'lemon juice', 'lemon zest', 'garlic', 'ginger', 'black pepper', 'sugar', 'water', 'salt', 'vegetable oil', 'soy sauce', 'soy sauce', 'vegetable stock', 'vegetable stock', 'vegetable stock', 'tofu']),
|
||||
('Vegan Carbonara', ['soy sauce', 'smoked tofu', 'lemon juice', 'nutritional yeast', 'mixed herbs', 'garlic', 'black pepper', 'silken tofu', 'turmeric', 'salt', 'olive oil', 'spaghetti', 'soy sauce', 'smoked tofu', 'smoked tofu', 'lemon juice', 'nutritional yeast', 'nutritional yeast', 'nutritional yeast']),
|
||||
('Vegan Pizza with Caramelized Onions', ['mushrooms', 'rosemary', 'garlic', 'red pepper flakes', 'yeast', 'barley malt', 'water', 'olive oil', 'garlic powder', 'oregano', 'honey', 'nutritional yeast', 'red onion', 'tomatoes', 'cashews', 'sugar', 'bell pepper', 'flour', 'salt', 'fresh basil', 'mushrooms', 'mushrooms', 'rosemary', 'rosemary', 'rosemary', 'garlic']),
|
||||
('Cheela with Spicy Mango Chutney', ['clove powder', 'oil', 'cinnamon powder', 'nigella seeds', 'curry leaves', 'coriander seeds', 'garlic', 'mangoes', 'mashed potatoes', 'cardamom powder', 'vinegar', 'water', 'mustard seeds', 'coriander powder', 'cumin powder', 'mango powder', 'garam masala', 'red chili powder', 'hing', 'garlic paste', 'turmeric powder', 'cilantro', 'sugar', 'onion', 'serrano chili', 'fresh ginger', 'turmeric', 'salt', 'fresh red chili', 'chickpea flour']),
|
||||
('Sweet and Spicy Crispy Green Beans', ['soy sauce', 'pomegranate molasses', 'sesame oil', 'green beans', 'sunflower oil', 'scallions', 'garlic', 'carrot', 'ginger', 'sesame seeds', 'tomato paste', 'bell pepper', 'siracha', 'soy sauce', 'soy sauce', 'pomegranate molasses', 'pomegranate molasses', 'pomegranate molasses', 'sesame oil', 'green beans', 'sunflower oil', 'sunflower oil', 'scallions', 'garlic', 'garlic', 'garlic']),
|
||||
('Vegan Mini Savory Mince Pies', ['mushrooms', 'cinnamon powder', 'rosemary', 'corn flour', 'ginger', 'brown sugar', 'carrot', 'black pepper', 'raisins', 'butternut squash', 'vegetarian worcestershire sauce', 'parev shortcrust pastry', 'olive oil', 'vegetable stock', 'dried cherries', 'lemon juice', 'lemon zest', 'figs', 'dried cranberries', 'apples', 'pecans', 'onion', 'orange juice', 'currants', 'dried blueberries', 'salt', 'brandy', 'orange zest', 'allspice powder']),
|
||||
('Roasted Corn and Zucchini Salad', ['green onions', 'lemon juice', 'lemon zest', 'dill', 'corn', 'tomatoes', 'black pepper', 'zucchini', 'olive oil', 'green onions', 'green onions', 'lemon juice', 'lemon juice', 'lemon juice', 'lemon zest']),
|
||||
('Golden Potato Salad', ['mustard seeds', 'cumin seeds', 'lemon juice', 'garlic', 'black pepper', 'balsamic vinegar', 'yukon gold potato', 'chives', 'turmeric', 'salt', 'olive oil', 'mustard seeds', 'cumin seeds', 'cumin seeds', 'lemon juice', 'garlic', 'garlic', 'garlic']),
|
||||
('Carrot Puff Pastry Tart', ['olive oil', 'lemon', 'lemon juice', 'pareve puff pastry', 'brown sugar', 'red onion', 'carrot', 'garlic', 'black pepper', 'thyme', 'vegan butter', 'water', 'salt', 'ground almonds', 'olive oil', 'olive oil', 'lemon', 'lemon', 'lemon', 'lemon juice']),
|
||||
|
||||
('Mushroom Lasagna', ['nutmeg', 'garlic', 'black pepper', 'onions', 'butter', 'parmesan cheese', 'portobello mushrooms', 'flour', 'dried lasagna noodles', 'olive oil', 'milk', 'kosher salt']),
|
||||
('Nut Wellington', ['sage', 'puff pastry sheets', 'hazelnuts', 'almonds', 'black pepper', 'thyme', 'marmite', 'breadcrumbs', 'walnuts', 'dates', 'eggs', 'olive oil', 'brazil nuts', 'leeks', 'chestnuts', 'cashews', 'apples', 'pecans', 'butter', 'salt', 'sage', 'sage', 'puff pastry sheets', 'puff pastry sheets', 'puff pastry sheets', 'hazelnuts', 'almonds', 'black pepper', 'black pepper', 'thyme', 'marmite', 'marmite', 'marmite']),
|
||||
('White Cheddar Scalloped Potatoes', ['shallots', 'garlic', 'cream', 'thyme', 'breadcrumbs', 'sharp white cheddar', 'yukon gold potato', 'milk', 'kosher salt']),
|
||||
('Winter Cobb Salad', ['smoked tofu', 'shiitake mushrooms', 'red wine vinegar', 'garlic', 'tomatoes', 'black pepper', 'avocados', 'blue cheese', 'onion', 'lacinato kale', 'eggs', 'olive oil', 'smoked tofu', 'smoked tofu', 'shiitake mushrooms', 'shiitake mushrooms', 'shiitake mushrooms', 'red wine vinegar']),
|
||||
('Roast Pumpkin and Lentil Salad with Roasted Lemon Dressing', ['honey', 'lemon', 'dijon mustard', 'feta cheese', 'red wine vinegar', 'red onion', 'watercress', 'green lentils', 'currants', 'capers', 'pepitas', 'fresh pumpkin', 'olive oil', 'honey', 'lemon', 'lemon', 'dijon mustard', 'feta cheese', 'feta cheese', 'feta cheese']),
|
||||
('Cambodian-Style Vegetable Spring Rolls', ['lime juice', 'toasted peanuts', 'firm tofu', 'garlic', 'corn flour', 'bean sprouts', 'carrot', 'palm sugar', 'shallots', 'red chili', 'vermicelli noodles', 'wombok', 'soy sauce', 'sunflower oil', 'spring roll wrappers', 'vegetarian fish sauce', 'lime juice', 'lime juice', 'toasted peanuts', 'toasted peanuts', 'toasted peanuts', 'firm tofu']),
|
||||
('Summer Minestrone Cups', ['fresh peas', 'rosemary', 'garlic', 'chickpeas', 'carrot', 'leek', 'green lentils', 'red chili', 'olive oil', 'croutons', 'fresh corn', 'lemon zest', 'green beans', 'parmesan rind', 'basil', 'tomatoes', 'vegetable bullion', 'parmesan cheese', 'celery', 'mint']),
|
||||
('Fried Zucchini with Balsamic and Chili Dressing', ['honey', 'lemon juice', 'garlic', 'red onion', 'red thai chili', 'pomegranate', 'balsamic vinegar', 'mint leaves', 'zucchini', 'olive oil', 'honey', 'honey', 'lemon juice', 'lemon juice', 'lemon juice', 'garlic', 'red onion', 'red thai chili', 'red thai chili', 'pomegranate', 'balsamic vinegar', 'balsamic vinegar', 'balsamic vinegar']),
|
||||
('Barley Risotto', ['sage', 'beets', 'pearl barley', 'brussel sprouts', 'rosemary', 'garlic', 'carrot', 'red onion', 'black pepper', 'thyme', 'butternut squash', 'vegetable bullion', 'parmesan cheese', 'olive oil', 'white wine']),
|
||||
('Cherry Tomato, Watercress and Fava Bean Salad', ['fresh peas', 'fresh cherry tomatoes', 'garlic', 'watercress', 'balsamic vinegar', 'fresh or frozen fava beans', 'fresh cherry bocconcini', 'salt', 'olive oil', 'fresh peas', 'fresh peas', 'fresh cherry tomatoes', 'fresh cherry tomatoes', 'fresh cherry tomatoes', 'garlic']),
|
||||
('Fresh Garden Peas over Cauliflower Almond Puree', ['red onions', 'lemon', 'fresh peas', 'garlic', 'grated nutmeg', 'cream', 'cauliflower', 'blanched almonds', 'fresh pea tendrils', 'olive oil', 'vegetable oil', 'red onions', 'lemon', 'lemon', 'fresh peas', 'garlic', 'garlic', 'garlic']),
|
||||
('Walnut Ravioli with Artichokes and Tomatoes', ['pine nuts', 'oil marinated artichokes', 'olives', 'rosemary', 'fresh tomatoes', 'ricotta cheese', 'black pepper', 'fresh artichoke hearts', 'walnuts', 'pasta sheets', 'lemon juice', 'lemon zest', 'basil', 'red onion', 'butter', 'salt', 'pine nuts', 'pine nuts', 'oil marinated artichokes', 'oil marinated artichokes', 'oil marinated artichokes', 'olives']),
|
||||
('Asparagus Puffs', ['eggs', 'asparagus', 'lemon juice', 'red onion', 'ricotta cheese', 'black pepper', 'thyme', 'salt', 'parmesan cheese', 'puff pastry', 'chives']),
|
||||
('Grilled Tofu Tacos', ['cotija cheese', 'masa', 'fresh cilantro leaves', 'jalapeño chili', 'chipotle chili', 'firm tofu', 'garlic', 'carrot', 'roasted corn', 'tomatillos', 'pepitas', 'ancho chili', 'crema', 'red onions', 'pasilla chili', 'lemon', 'hot water', 'lemon juice', 'tomatoes', 'avocado', 'cumin', 'red cabbage', 'limes', 'black beans', 'cotija cheese', 'cotija cheese', 'masa', 'masa', 'masa', 'fresh cilantro leaves', 'jalapeño chili', 'chipotle chili', 'chipotle chili', 'firm tofu', 'garlic', 'garlic', 'garlic']),
|
||||
|
||||
('Zucchini Fritters with Lemon-Thyme Coconut Yogurt', ['baking soda', 'coconut flour', 'lemon juice', 'lemon zest', 'eggs', 'coconut yogurt', 'black pepper', 'fresh thai chili', 'coconut oil', 'chives', 'zucchini', 'salt']),
|
||||
('Avocado Deviled Eggs', ['lime juice', 'fresh cilantro leaves', 'eggs', 'seranno chili', 'avocado', 'pepitas', 'salt', 'garlic powder', 'lime juice', 'lime juice', 'fresh cilantro leaves', 'fresh cilantro leaves', 'fresh cilantro leaves', 'eggs']),
|
||||
('Grilled Flank Steak with Caesar Salad', ['pine nuts', 'white vinegar', 'chipotle chili', 'scallions', 'garlic', 'paleo parmesan cheese', 'black pepper', 'avocado oil', 'treviso', 'olive oil', 'radishes', 'flank steak', 'dijon mustard', 'castelfranco radicchio', 'worcestershire sauce', 'fresh parsley', 'cherry tomatoes', 'salt', 'avocado mayonnaise', 'pine nuts', 'white vinegar', 'white vinegar', 'chipotle chili', 'scallions', 'scallions', 'scallions']),
|
||||
('Pumpkin Bread Crostini', ['coconut flour', 'garlic', 'black pepper', 'cloves', 'eggs', 'olive oil', 'onions', 'honey', 'apple cider vinegar', 'almond butter', 'baking soda', 'nutmeg', 'pumpkin puree', 'cinnamon', 'shrimp', 'basil', 'coconut oil', 'cherry tomatoes', 'salt', 'fresh red chili', 'coconut flour', 'coconut flour', 'garlic', 'garlic', 'garlic', 'black pepper']),
|
||||
('BLT Bites', ['mustard seed', 'onion', 'kale', 'cherry tomatoes', 'bacon', 'paleo mayonnaise']),
|
||||
('Roasted Chicken with Roasted Tomatoes, Avocado, and Sweet Potatoes', ['purple sweet potato', 'chiles de árbol', 'garlic', 'tomatoes', 'safflower oil', 'black pepper', 'mexican oregano', 'avocados', 'shallots', 'cumin', 'olive oil', 'lemons', 'whole chicken', 'limes', 'kosher salt', 'purple sweet potato', 'purple sweet potato', 'chiles de árbol', 'chiles de árbol', 'chiles de árbol', 'garlic', 'tomatoes', 'safflower oil', 'safflower oil', 'black pepper', 'mexican oregano', 'mexican oregano', 'mexican oregano']),
|
||||
('Chicken with Tamarind and Apricot Sauce', ['garlic', 'black pepper', 'dried apricots', 'roma tomatoes', 'water', 'olive oil', 'honey', 'cinnamon', 'ground cumin', 'cider vinegar', 'chili powder', 'safflower oil', 'homemade tamarind concentrate', 'white chicken', 'allspice', 'chipotles', 'salt', 'homemade apricot honey preserves', 'kosher salt']),
|
||||
('Grilled Fish Tacos with Cauliflower Tortillas', ['green onions', 'serrano chili', 'shredded red cabbage', 'smoked paprika', 'mango', 'eggs', 'black pepper', 'cilantro', 'cauliflower', 'avocado', 'lime', 'cumin', 'salt', 'tilapia', 'green onions', 'green onions', 'serrano chili', 'serrano chili', 'serrano chili', 'shredded red cabbage']),
|
||||
('Grilled Pork Chops with Mango Pineapple Salsa', ['cilantro leaves', 'lime', 'pineapple', 'chipotle chili', 'garlic', 'mangoes', 'cauliflower', 'avocado', 'serrano chili', 'pork chops', 'lime zest', 'lacinato kale', 'onions', 'cilantro leaves', 'lime', 'lime', 'pineapple', 'chipotle chili', 'chipotle chili', 'chipotle chili']),
|
||||
('Grilled Shrimp and Pesto over Zucchini Noodles', ['pine nuts', 'shrimp', 'green bell pepper', 'lemon juice', 'basil', 'lemon zest', 'garlic', 'tomatoes', 'cashews', 'yellow bell pepper', 'cumin', 'zucchini', 'salt', 'olive oil', 'red bell pepper', 'pine nuts', 'pine nuts', 'shrimp', 'shrimp', 'shrimp', 'green bell pepper']),
|
||||
|
||||
('Cauliflower Pizza with Roasted Tomatoes and Chicken', ['parmesan', 'almond meal', 'rosemary', 'mozzarella cheese', 'roasted chicken', 'tomato paste', 'cauliflower', 'cherry tomatoes', 'eggs', 'fresh basil', 'olive oil']),
|
||||
('Flank Steak with Chimichurri and Asparagus', ['white vinegar', 'asparagus', 'flank steak', 'chipotle chili', 'scallions', 'garlic', 'black pepper', 'cauliflower', 'sour cream', 'fresh parsley', 'salt', 'olive oil', 'white vinegar', 'white vinegar', 'asparagus', 'asparagus', 'asparagus', 'flank steak', 'chipotle chili', 'scallions', 'scallions', 'garlic', 'black pepper', 'black pepper', 'black pepper']),
|
||||
('Kingfish Lettuce Cups', ['soy sauce', 'watermelon radishes', 'lime juice', 'fish sauce', 'mirin', 'little gem lettuce heads', 'peanut oil', 'garlic', 'sesame seeds', 'oyster sauce', 'spring onions', 'avocado', 'grilled king fish', 'red cabbage']),
|
||||
('Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney', ['clove powder', 'curry leaves', 'coriander seeds', 'ginger', 'cardamom powder', 'vinegar', 'mustard seeds', 'mango powder', 'garam masala', 'red chili powder', 'chicken', 'hing', 'turmeric powder', 'tomatoes', 'ginger garlic paste', 'fresh greek yogurt', 'fresh ginger', 'monk fruit', 'turmeric', 'cashew nuts', 'salt', 'fresh red chili', 'cinnamon powder', 'nigella seeds', 'whole small crimini mushrooms', 'brussel sprouts', 'garlic', 'mangoes', 'heavy cream', 'cloves', 'coriander powder', 'cumin powder', 'cardamom', 'green chili', 'cinnamon', 'garlic paste', 'red chili flakes', 'lemon juice', 'cilantro', 'butter', 'dried fenugreek leaves', 'boned chicken', 'clove powder', 'clove powder', 'curry leaves', 'curry leaves', 'curry leaves', 'coriander seeds']),
|
||||
('Prawn and Herb Omelette', ['green onions', 'parsley', 'sesame seeds', 'black pepper', 'chives', 'eggs', 'fennel bulb', 'tahini', 'olive oil', 'harissa', 'shrimp', 'lemon juice', 'dill', 'butter', 'onion', 'fresh cucumber', 'monk fruit', 'salt', 'green onions', 'parsley', 'parsley', 'sesame seeds', 'black pepper', 'black pepper', 'black pepper']),
|
||||
('Satay Steak Skewers', ['sriacha', 'apple cider vinegar', 'lime juice', 'fish sauce', 'red and green thai chili', 'flank steak', 'carrot', 'peanuts', 'cucumbers', 'roasted peanuts', 'crunchy peanut butter', 'kecap manis', 'monk fruit', 'micro cilantro', 'olive oil', 'sriacha', 'sriacha', 'apple cider vinegar', 'apple cider vinegar', 'apple cider vinegar', 'lime juice']),
|
||||
('Parmesan Crackers and Spinach Crackers with Salmon Pate', ['spinach', 'parmesan cheese', 'coconut flour', 'chili flakes', 'garlic', 'black pepper', 'cream cheese', 'ghee', 'lemon juice', 'flaxmeal', 'red onion', 'salt', 'salmon fillets', 'pecans', 'almond flour', 'cumin', 'fresh cucumber', 'cherry tomatoes', 'chives', 'avocado mayonnaise']),
|
||||
('Pork Chops with Grilled Castelfranco Radicchio and Asparagus', ['rosemary', 'garlic', 'black pepper', 'thyme', 'avocado oil', 'eggs', 'oregano', 'asparagus', 'lemon', 'dijon mustard', 'basil', 'castelfranco radicchio', 'dill', 'butter', 'pork chops', 'monk fruit', 'salt', 'avocado mayonnaise', 'rosemary', 'rosemary', 'garlic', 'garlic', 'garlic', 'black pepper', 'thyme', 'avocado oil', 'avocado oil', 'eggs', 'oregano', 'oregano', 'oregano']),
|
||||
('Seared Salmon with Pickled Vegetable and Watercress Salad', ['lime juice', 'caster sugar', 'toasted buckwheat', 'lemon juice', 'red wine vinegar', 'red onion', 'dutch carrot', 'salmon steaks', 'watercress', 'pink peppercorns', 'shallots', 'fennel seeds', 'red cabbage', 'radishes']),
|
||||
('Braised Pork Belly with Apple Salad', ['cilantro leaves', 'green cabbage', 'lemon juice', 'pork belly', 'dark soy sauce', 'granny smith apples', 'ginger', 'light soy sauce', 'sesame seeds', 'black pepper', 'cinnamon sticks', 'spring onions', 'star anise', 'monk fruit', 'olive oil', 'cilantro leaves', 'cilantro leaves', 'green cabbage', 'green cabbage', 'green cabbage', 'lemon juice']),
|
||||
|
||||
('Beachside Snapper', ['lime juice', 'salsa', 'green bell pepper', 'anaheim chili', 'black pepper', 'olive oil', 'yellow mustard', 'red bell pepper', 'soy sauce', 'mexican crema', 'mayonnaise', 'white onion', 'garlic cloves', 'fresh corn tortillas', 'red onion', 'tomatoes', 'limes', 'worcestershire sauce', 'butter', 'yellow bell pepper', 'salt', 'red snapper', 'lime juice', 'salsa', 'salsa', 'green bell pepper', 'anaheim chili', 'anaheim chili', 'anaheim chili']),
|
||||
('Governor Shrimp Tacos', ['lime juice', 'poblano chili', 'tomato paste', 'black pepper', 'chipotle adobo sauce', 'chile manzano', 'roma tomatoes', 'pepitas', 'oaxaca cheese', 'white onion', 'shelled large shrimp', 'garlic cloves', 'fresh corn tortillas', 'red onion', 'worcestershire sauce', 'avocado', 'butter', 'kosher salt', 'lime juice', 'lime juice', 'poblano chili', 'poblano chili', 'poblano chili', 'tomato paste']),
|
||||
('Shrimp, Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', ['fresh tortillas', 'shrimp', 'garlic', 'chickpeas', 'black pepper', 'slivered almonds', 'guajillo chile', 'bacon', 'sea salt', 'butter', 'onion', 'avocado', 'olive oil']),
|
||||
('Burnt Masala Wings with Classic Coleslaw', ['lime juice', 'green cabbage', 'chiles de árbol', 'green cardamom', 'ginger', 'carrot', 'sriracha', 'black pepper', 'cinnamon sticks', 'celery seeds', 'cloves', 'black cardamom', 'olive oil', 'chicken wings', 'apple cider vinegar', 'mayonnaise', 'honey', 'black peppercorns', 'lemon juice', 'garlic cloves', 'tamarind concentrate', 'cilantro', 'butter', 'serrano chili', 'red cabbage', 'kosher salt', 'lime juice', 'lime juice', 'green cabbage', 'green cabbage', 'green cabbage', 'chiles de árbol', 'green cardamom', 'ginger', 'ginger', 'carrot', 'sriracha', 'sriracha', 'sriracha']),
|
||||
('Dahi Puri with Black Chickpeas', ['whole-milk yogurt', 'chaat masala', 'ginger', 'pani puri', 'scallion chutney', 'yukon gold potato', 'black chickpeas', 'thin sev', 'date syrup', 'red onion', 'roasted chicken', 'chili powder', 'tamarind concentrate', 'cumin', 'turmeric', 'mint']),
|
||||
('Brisket with Grilled Rhubarb, Onions, and Fennel', ['white vinegar', 'parsley', 'yellow onion', 'rhubarb', 'garlic', 'brown sugar', 'black pepper', 'thyme', 'crushed red pepper flakes', 'fennel bulbs', 'beer', 'marjoram', 'vegetable oil', 'soy sauce', 'oregano', 'lemon', 'red onion', 'chili powder', 'cilantro', 'beef brisket', 'balsamic vinegar', 'worcestershire sauce', 'celery', 'mint', 'kosher salt', 'white vinegar', 'white vinegar', 'parsley', 'parsley', 'parsley', 'yellow onion']),
|
||||
('Roast Leg of Lamb with Crispy Moroccan Carrots & Couscous', ['oranges', 'rosemary', 'garlic', 'anchovy fillets', 'couscous', 'carrot', 'sesame seeds', 'fresh thyme', 'vinegar', 'olive oil', 'tahini', 'harissa', 'onions', 'honey', 'fresh mint', 'leg of lamb', 'tomatoes', 'baby carrot', 'vegetable bullion', 'filo pastry', 'celery', 'bay leaves', 'yoghurt', 'oranges', 'rosemary', 'rosemary', 'garlic', 'anchovy fillets', 'anchovy fillets', 'anchovy fillets']),
|
||||
('Baked Chicken Jollof Rice', ['tomato puree', 'garlic', 'ginger', 'tomato', 'carrot', 'thyme', 'white pepper', 'water', 'maggi cubes', 'chicken', 'rice', 'coriander', 'scotch bonnet pepper', 'bell pepper', 'onion', 'turmeric', 'salt', 'tomato puree', 'tomato puree', 'garlic', 'garlic', 'garlic', 'ginger']),
|
||||
('Seafood Risotto', ['garlic', 'tomato paste', 'baby scallops', 'mussels', 'water', 'crab legs', 'olive oil', 'baby squid', 'fish stock', 'lemon juice', 'white onion', 'arborio risotto rice', 'clams', 'parmesan cheese', 'flat-leaf parsley', 'cherry tomatoes', 'prawns', 'white wine']),
|
||||
('Lamb over Marinated Summer Squash with Hazelnuts and Ricotta', ['toasted bread', 'rosemary', 'hazelnuts', 'anchovy fillets', 'garlic', 'carrot', 'summer squash', 'black pepper', 'red pepper flakes', 'vinegar', 'sea salt', 'zucchini', 'olive oil', 'onions', 'white wine vinegar', 'fresh mint', 'leg of lamb', 'lemon', 'fresh ricotta', 'sugar', 'celery', 'bay leaves', 'kosher salt', 'toasted bread', 'toasted bread', 'rosemary', 'rosemary', 'rosemary', 'hazelnuts', 'anchovy fillets', 'garlic', 'garlic', 'carrot', 'summer squash', 'summer squash', 'summer squash'])
|
||||
]
|
||||
|
||||
recipes_without_duplicates = [('kisir_with_warm_pita', 'Kisir with Warm Pita', {'water', 'chopped parsley', 'pomegranate molasses', 'spring onion', 'lemon juice', 'olive oil', 'bulgur', 'tomato paste', 'tomato', 'persian cucumber'}),
|
||||
('shakshuka', 'Shakshuka', {'smoked paprika', 'tomatoes', 'cumin', 'firm tofu', 'olive oil', 'harissa', 'yellow onion', 'sugar', "za'atar", 'tomato paste', 'cloves', 'red bell pepper', 'vegan unsweetened yoghurt'}),
|
||||
('vegetarian_khoresh_bademjan', 'Vegetarian Khoresh Bademjan', {'pomegranate concentrate', 'saffron powder', 'cayenne pepper', 'barberries', 'salt', 'yellow onions', 'yellow split peas', 'slivered almonds', 'ground turmeric', 'orange zest', 'lemon juice', 'kosher salt', 'hot water', 'basmati rice', 'black pepper', 'orange juice', 'tomato paste', 'vegan butter', 'chinese eggplants'}),
|
||||
('baked_kelewele', 'Baked Kelewele', {'salt', 'coconut oil', 'smoked paprika', 'cayenne pepper', 'red onion', 'fresh ginger', 'calabash nutmeg', 'grains of selim', 'black peppercorn', 'cloves', 'ripe plantains'}),
|
||||
('waakye', 'Waakye', {'salt', 'coconut oil', 'water', 'white rice', 'sorghum stems', 'black-eyed peas', 'baking soda'}),
|
||||
('georgian_eggplant_rolls_with_walnuts', 'Georgian Eggplant Rolls with Walnuts', {'salt', 'coriander', 'pomegranate seeds', 'oil', 'water', 'vinegar', 'garlic', 'eggplants', 'black pepper', 'khmeli suneli', 'walnuts'}),
|
||||
('burmese_tofu_with_garlic,_ginger_and_chili_sauce', 'Burmese Tofu with Garlic, Ginger and Chili Sauce', {'salt', 'rice vinegar', 'oil', 'water', 'turmeric', 'chili flakes', 'spring onions', 'brown sugar', 'garlic', 'chickpea flour', 'ginger', 'soy sauce', 'peanuts'}),
|
||||
('celeriac_schnitzel', 'Celeriac Schnitzel', {'salt', 'parsley', 'water', 'sunflower oil', 'flour', 'breadcrumbs', 'lemon', 'celeriac', 'chickpea flour', 'soy sauce', 'black pepper'}),
|
||||
('sticky_lemon_tofu', 'Sticky Lemon Tofu', {'salt', 'water', 'tofu', 'cornstarch', 'vegetable stock', 'vegetable oil', 'lemon juice', 'garlic', 'black pepper', 'ginger', 'soy sauce', 'sugar', 'lemon zest'}),
|
||||
('vegan_carbonara', 'Vegan Carbonara', {'salt', 'turmeric', 'spaghetti', 'lemon juice', 'olive oil', 'garlic', 'black pepper', 'nutritional yeast', 'soy sauce', 'smoked tofu', 'silken tofu', 'mixed herbs'}),
|
||||
('vegan_pizza_with_caramelized_onions', 'Vegan Pizza with Caramelized Onions', {'red pepper flakes', 'garlic', 'cashews', 'fresh basil', 'flour', 'tomatoes', 'red onion', 'oregano', 'nutritional yeast', 'rosemary', 'water', 'bell pepper', 'honey', 'barley malt', 'olive oil', 'salt', 'garlic powder', 'mushrooms', 'yeast', 'sugar'}),
|
||||
('cheela_with_spicy_mango_chutney', 'Cheela with Spicy Mango Chutney', {'hing', 'curry leaves', 'mangoes', 'garlic', 'nigella seeds', 'garlic paste', 'coriander seeds', 'oil', 'turmeric', 'red chili powder', 'coriander powder', 'cardamom powder', 'turmeric powder', 'clove powder', 'water', 'cumin powder', 'fresh ginger', 'vinegar', 'cinnamon powder', 'cilantro', 'chickpea flour', 'onion', 'salt', 'mango powder', 'fresh red chili', 'mashed potatoes', 'mustard seeds', 'serrano chili', 'garam masala', 'sugar'}),
|
||||
('sweet_and_spicy_crispy_green_beans', 'Sweet and Spicy Crispy Green Beans', {'sesame oil', 'siracha', 'sunflower oil', 'pomegranate molasses', 'sesame seeds', 'green beans', 'carrot', 'scallions', 'garlic', 'ginger', 'soy sauce', 'tomato paste', 'bell pepper'}),
|
||||
('vegan_mini_savory_mince_pies', 'Vegan Mini Savory Mince Pies', {'apples', 'brandy', 'pecans', 'raisins', 'brown sugar', 'lemon zest', 'dried blueberries', 'vegetarian worcestershire sauce', 'orange zest', 'butternut squash', 'corn flour', 'rosemary', 'vegetable stock', 'carrot', 'figs', 'cinnamon powder', 'olive oil', 'orange juice', 'black pepper', 'onion', 'dried cherries', 'salt', 'dried cranberries', 'parev shortcrust pastry', 'mushrooms', 'allspice powder', 'lemon juice', 'ginger', 'currants'}),
|
||||
('roasted_corn_and_zucchini_salad', 'Roasted Corn and Zucchini Salad', {'zucchini', 'tomatoes', 'green onions', 'corn', 'lemon juice', 'olive oil', 'black pepper', 'lemon zest', 'dill'}),
|
||||
('golden_potato_salad', 'Golden Potato Salad', {'salt', 'yukon gold potato', 'turmeric', 'balsamic vinegar', 'cumin seeds', 'lemon juice', 'olive oil', 'garlic', 'mustard seeds', 'black pepper', 'chives'}),
|
||||
('carrot_puff_pastry_tart', 'Carrot Puff Pastry Tart', {'salt', 'water', 'lemon', 'thyme', 'red onion', 'carrot', 'lemon juice', 'pareve puff pastry', 'ground almonds', 'brown sugar', 'olive oil', 'garlic', 'black pepper', 'vegan butter'}),
|
||||
|
||||
('mushroom_lasagna', 'Mushroom Lasagna', {'dried lasagna noodles', 'portobello mushrooms', 'nutmeg', 'flour', 'kosher salt', 'milk', 'olive oil', 'garlic', 'onions', 'butter', 'black pepper', 'parmesan cheese'}),
|
||||
('nut_wellington', 'Nut Wellington', {'apples', 'eggs', 'hazelnuts', 'pecans', 'thyme', 'dates', 'cashews', 'breadcrumbs', 'almonds', 'marmite', 'sage', 'leeks', 'olive oil', 'black pepper', 'walnuts', 'salt', 'chestnuts', 'brazil nuts', 'butter', 'puff pastry sheets'}),
|
||||
('white_cheddar_scalloped_potatoes', 'White Cheddar Scalloped Potatoes', {'yukon gold potato', 'shallots', 'cream', 'thyme', 'breadcrumbs', 'sharp white cheddar', 'kosher salt', 'milk', 'garlic'}),
|
||||
('winter_cobb_salad', 'Winter Cobb Salad', {'lacinato kale', 'eggs', 'red wine vinegar', 'shiitake mushrooms', 'tomatoes', 'avocados', 'olive oil', 'blue cheese', 'garlic', 'black pepper', 'onion', 'smoked tofu'}),
|
||||
('roast_pumpkin_and_lentil_salad_with_roasted_lemon_dressing', 'Roast Pumpkin and Lentil Salad with Roasted Lemon Dressing', {'red wine vinegar', 'dijon mustard', 'lemon', 'honey', 'red onion', 'feta cheese', 'olive oil', 'capers', 'pepitas', 'fresh pumpkin', 'watercress', 'green lentils', 'currants'}),
|
||||
('cambodian-style_vegetable_spring_rolls', 'Cambodian-Style Vegetable Spring Rolls', {'sunflower oil', 'vegetarian fish sauce', 'shallots', 'vermicelli noodles', 'wombok', 'firm tofu', 'lime juice', 'corn flour', 'palm sugar', 'bean sprouts', 'spring roll wrappers', 'garlic', 'red chili', 'soy sauce', 'toasted peanuts', 'carrot'}),
|
||||
('summer_minestrone_cups', 'Summer Minestrone Cups', {'leek', 'garlic', 'lemon zest', 'tomatoes', 'red chili', 'chickpeas', 'rosemary', 'carrot', 'vegetable bullion', 'fresh corn', 'fresh peas', 'green beans', 'olive oil', 'parmesan cheese', 'croutons', 'celery', 'mint', 'basil', 'green lentils', 'parmesan rind'}),
|
||||
('fried_zucchini_with_balsamic_and_chili_dressing', 'Fried Zucchini with Balsamic and Chili Dressing', {'zucchini', 'balsamic vinegar', 'honey', 'red onion', 'lemon juice', 'olive oil', 'mint leaves', 'garlic', 'red thai chili', 'pomegranate'}),
|
||||
('barley_risotto', 'Barley Risotto', {'beets', 'white wine', 'thyme', 'red onion', 'carrot', 'butternut squash', 'olive oil', 'sage', 'garlic', 'brussel sprouts', 'rosemary', 'black pepper', 'parmesan cheese', 'pearl barley', 'vegetable bullion'}),
|
||||
('cherry_tomato,_watercress_and_fava_bean_salad', 'Cherry Tomato, Watercress and Fava Bean Salad', {'salt', 'fresh or frozen fava beans', 'fresh peas', 'balsamic vinegar', 'olive oil', 'garlic', 'watercress', 'fresh cherry bocconcini', 'fresh cherry tomatoes'}),
|
||||
('fresh_garden_peas_over_cauliflower_almond_puree', 'Fresh Garden Peas over Cauliflower Almond Puree', {'grated nutmeg', 'red onions', 'lemon', 'fresh peas', 'cream', 'vegetable oil', 'olive oil', 'garlic', 'blanched almonds', 'cauliflower', 'fresh pea tendrils'}),
|
||||
('walnut_ravioli_with_artichokes_and_tomatoes', 'Walnut Ravioli with Artichokes and Tomatoes', {'salt', 'fresh tomatoes', 'ricotta cheese', 'lemon zest', 'olives', 'red onion', 'lemon juice', 'black pepper', 'rosemary', 'butter', 'basil', 'fresh artichoke hearts', 'pasta sheets', 'pine nuts', 'walnuts', 'oil marinated artichokes'}),
|
||||
('asparagus_puffs', 'Asparagus Puffs', {'salt', 'asparagus', 'ricotta cheese', 'thyme', 'puff pastry', 'red onion', 'eggs', 'lemon juice', 'black pepper', 'parmesan cheese', 'chives'}),
|
||||
('grilled_tofu_tacos', 'Grilled Tofu Tacos', {'carrot', 'red cabbage', 'ancho chili', 'limes', 'hot water', 'garlic', 'roasted corn', 'masa', 'pasilla chili', 'lemon', 'tomatoes', 'cumin', 'fresh cilantro leaves', 'chipotle chili', 'red onions', 'pepitas', 'tomatillos', 'black beans', 'cotija cheese', 'crema', 'firm tofu', 'lemon juice', 'jalapeño chili', 'avocado'}),
|
||||
|
||||
('zucchini_fritters_with_lemon-thyme_coconut_yogurt', 'Zucchini Fritters with Lemon-Thyme Coconut Yogurt', {'salt', 'coconut oil', 'eggs', 'fresh thai chili', 'coconut yogurt', 'zucchini', 'lemon juice', 'black pepper', 'coconut flour', 'chives', 'lemon zest', 'baking soda'}),
|
||||
('avocado_deviled_eggs', 'Avocado Deviled Eggs', {'salt', 'eggs', 'garlic powder', 'seranno chili', 'avocado', 'lime juice', 'pepitas', 'fresh cilantro leaves'}),
|
||||
('grilled_flank_steak_with_caesar_salad', 'Grilled Flank Steak with Caesar Salad', {'flank steak', 'avocado oil', 'treviso', 'scallions', 'garlic', 'cherry tomatoes', 'pine nuts', 'white vinegar', 'chipotle chili', 'dijon mustard', 'olive oil', 'avocado mayonnaise', 'black pepper', 'paleo parmesan cheese', 'castelfranco radicchio', 'fresh parsley', 'salt', 'worcestershire sauce', 'radishes'}),
|
||||
('pumpkin_bread_crostini', 'Pumpkin Bread Crostini', {'eggs', 'nutmeg', 'cinnamon', 'garlic', 'pumpkin puree', 'apple cider vinegar', 'cherry tomatoes', 'onions', 'coconut flour', 'cloves', 'shrimp', 'coconut oil', 'honey', 'olive oil', 'black pepper', 'almond butter', 'salt', 'fresh red chili', 'basil', 'baking soda'}),
|
||||
('blt_bites', 'BLT Bites', {'paleo mayonnaise', 'bacon', 'kale', 'cherry tomatoes', 'mustard seed', 'onion'}),
|
||||
('roasted_chicken_with_roasted_tomatoes,_avocado,_and_sweet_potatoes', 'Roasted Chicken with Roasted Tomatoes, Avocado, and Sweet Potatoes', {'safflower oil', 'shallots', 'whole chicken', 'lemons', 'tomatoes', 'purple sweet potato', 'avocados', 'limes', 'kosher salt', 'olive oil', 'mexican oregano', 'garlic', 'black pepper', 'cumin', 'chiles de árbol'}),
|
||||
('chicken_with_tamarind_and_apricot_sauce', 'Chicken with Tamarind and Apricot Sauce', {'cinnamon', 'garlic', 'cider vinegar', 'chipotles', 'homemade tamarind concentrate', 'roma tomatoes', 'white chicken', 'ground cumin', 'water', 'safflower oil', 'allspice', 'dried apricots', 'honey', 'olive oil', 'black pepper', 'salt', 'homemade apricot honey preserves', 'kosher salt', 'chili powder'}),
|
||||
('grilled_fish_tacos_with_cauliflower_tortillas', 'Grilled Fish Tacos with Cauliflower Tortillas', {'salt', 'eggs', 'smoked paprika', 'shredded red cabbage', 'mango', 'green onions', 'lime', 'cumin', 'tilapia', 'cilantro', 'black pepper', 'serrano chili', 'cauliflower', 'avocado'}),
|
||||
('grilled_pork_chops_with_mango_pineapple_salsa', 'Grilled Pork Chops with Mango Pineapple Salsa', {'pork chops', 'lacinato kale', 'chipotle chili', 'serrano chili', 'lime zest', 'pineapple', 'lime', 'garlic', 'onions', 'mangoes', 'cauliflower', 'avocado', 'cilantro leaves'}),
|
||||
('grilled_shrimp_and_pesto_over_zucchini_noodles', 'Grilled Shrimp and Pesto over Zucchini Noodles', {'salt', 'green bell pepper', 'cashews', 'zucchini', 'tomatoes', 'red bell pepper', 'cumin', 'lemon juice', 'yellow bell pepper', 'olive oil', 'garlic', 'pine nuts', 'basil', 'lemon zest', 'shrimp'}),
|
||||
|
||||
('cauliflower_pizza_with_roasted_tomatoes_and_chicken', 'Cauliflower Pizza with Roasted Tomatoes and Chicken', {'roasted chicken', 'eggs', 'fresh basil', 'olive oil', 'almond meal', 'cherry tomatoes', 'rosemary', 'cauliflower', 'mozzarella cheese', 'tomato paste', 'parmesan'}),
|
||||
('flank_steak_with_chimichurri_and_asparagus', 'Flank Steak with Chimichurri and Asparagus', {'fresh parsley', 'salt', 'asparagus', 'chipotle chili', 'flank steak', 'scallions', 'olive oil', 'garlic', 'black pepper', 'cauliflower', 'white vinegar', 'sour cream'}),
|
||||
('kingfish_lettuce_cups', 'Kingfish Lettuce Cups', {'oyster sauce', 'fish sauce', 'spring onions', 'sesame seeds', 'grilled king fish', 'little gem lettuce heads', 'mirin', 'red cabbage', 'lime juice', 'peanut oil', 'garlic', 'watermelon radishes', 'soy sauce', 'avocado'}),
|
||||
('butter_chicken_with_grilled_mushrooms,_brussel_sprouts_and_mango_chutney', 'Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney', {'hing', 'curry leaves', 'cashew nuts', 'green chili', 'cinnamon', 'mangoes', 'garlic', 'nigella seeds', 'garlic paste', 'coriander seeds', 'turmeric', 'ginger garlic paste', 'tomatoes', 'red chili powder', 'coriander powder', 'cardamom powder', 'turmeric powder', 'clove powder', 'cloves', 'heavy cream', 'cardamom', 'fresh greek yogurt', 'monk fruit', 'fresh ginger', 'cumin powder', 'vinegar', 'cinnamon powder', 'cilantro', 'brussel sprouts', 'whole small crimini mushrooms', 'salt', 'boned chicken', 'red chili flakes', 'mango powder', 'fresh red chili', 'lemon juice', 'garam masala', 'butter', 'ginger', 'mustard seeds', 'chicken', 'dried fenugreek leaves'}),
|
||||
('prawn_and_herb_omelette', 'Prawn and Herb Omelette', {'salt', 'parsley', 'eggs', 'fresh cucumber', 'sesame seeds', 'green onions', 'monk fruit', 'lemon juice', 'olive oil', 'tahini', 'harissa', 'black pepper', 'butter', 'onion', 'fennel bulb', 'chives', 'shrimp', 'dill'}),
|
||||
('satay_steak_skewers', 'Satay Steak Skewers', {'flank steak', 'red and green thai chili', 'kecap manis', 'fish sauce', 'monk fruit', 'micro cilantro', 'carrot', 'apple cider vinegar', 'sriacha', 'lime juice', 'olive oil', 'cucumbers', 'roasted peanuts', 'crunchy peanut butter', 'peanuts'}),
|
||||
('parmesan_crackers_and_spinach_crackers_with_salmon_pate', 'Parmesan Crackers and Spinach Crackers with Salmon Pate', {'fresh cucumber', 'pecans', 'ghee', 'garlic', 'chives', 'flaxmeal', 'chili flakes', 'almond flour', 'salmon fillets', 'red onion', 'cherry tomatoes', 'coconut flour', 'cumin', 'spinach', 'cream cheese', 'avocado mayonnaise', 'black pepper', 'parmesan cheese', 'salt', 'lemon juice'}),
|
||||
('pork_chops_with_grilled_castelfranco_radicchio_and_asparagus', 'Pork Chops with Grilled Castelfranco Radicchio and Asparagus', {'pork chops', 'salt', 'asparagus', 'eggs', 'dijon mustard', 'avocado oil', 'lemon', 'thyme', 'monk fruit', 'oregano', 'avocado mayonnaise', 'garlic', 'black pepper', 'rosemary', 'butter', 'basil', 'castelfranco radicchio', 'dill'}),
|
||||
('seared_salmon_with_pickled_vegetable_and_watercress_salad', 'Seared Salmon with Pickled Vegetable and Watercress Salad', {'red wine vinegar', 'caster sugar', 'salmon steaks', 'radishes', 'shallots', 'red onion', 'pink peppercorns', 'toasted buckwheat', 'lemon juice', 'lime juice', 'red cabbage', 'watercress', 'fennel seeds', 'dutch carrot'}),
|
||||
('braised_pork_belly_with_apple_salad', 'Braised Pork Belly with Apple Salad', {'spring onions', 'dark soy sauce', 'light soy sauce', 'pork belly', 'monk fruit', 'green cabbage', 'star anise', 'sesame seeds', 'lemon juice', 'olive oil', 'black pepper', 'ginger', 'cinnamon sticks', 'granny smith apples', 'cilantro leaves'}),
|
||||
|
||||
('beachside_snapper', 'Beachside Snapper', {'yellow mustard', 'mayonnaise', 'limes', 'white onion', 'red snapper', 'fresh corn tortillas', 'tomatoes', 'red onion', 'lime juice', 'soy sauce', 'mexican crema', 'salsa', 'garlic cloves', 'green bell pepper', 'olive oil', 'black pepper', 'salt', 'yellow bell pepper', 'worcestershire sauce', 'anaheim chili', 'butter', 'red bell pepper'}),
|
||||
('governor_shrimp_tacos', 'Governor Shrimp Tacos', {'chile manzano', 'worcestershire sauce', 'oaxaca cheese', 'garlic cloves', 'red onion', 'avocado', 'lime juice', 'kosher salt', 'white onion', 'chipotle adobo sauce', 'poblano chili', 'fresh corn tortillas', 'butter', 'black pepper', 'pepitas', 'tomato paste', 'roma tomatoes', 'shelled large shrimp'}),
|
||||
('shrimp,_bacon_and_crispy_chickpea_tacos_with_salsa_de_guacamole', 'Shrimp, Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', {'fresh tortillas', 'slivered almonds', 'bacon', 'sea salt', 'chickpeas', 'olive oil', 'guajillo chile', 'garlic', 'butter', 'black pepper', 'onion', 'shrimp', 'avocado'}),
|
||||
('burnt_masala_wings_with_classic_coleslaw', 'Burnt Masala Wings with Classic Coleslaw', {'mayonnaise', 'carrot', 'red cabbage', 'green cabbage', 'apple cider vinegar', 'lime juice', 'cloves', 'cinnamon sticks', 'sriracha', 'celery seeds', 'chicken wings', 'garlic cloves', 'honey', 'olive oil', 'cilantro', 'black pepper', 'chiles de árbol', 'tamarind concentrate', 'green cardamom', 'black cardamom', 'lemon juice', 'kosher salt', 'serrano chili', 'ginger', 'black peppercorns', 'butter'}),
|
||||
('dahi_puri_with_black_chickpeas', 'Dahi Puri with Black Chickpeas', {'roasted chicken', 'scallion chutney', 'yukon gold potato', 'turmeric', 'black chickpeas', 'whole-milk yogurt', 'chili powder', 'red onion', 'pani puri', 'ginger', 'mint', 'thin sev', 'date syrup', 'cumin', 'chaat masala', 'tamarind concentrate'}),
|
||||
('brisket_with_grilled_rhubarb,_onions,_and_fennel', 'Brisket with Grilled Rhubarb, Onions, and Fennel', {'crushed red pepper flakes', 'rhubarb', 'thyme', 'yellow onion', 'garlic', 'brown sugar', 'beer', 'lemon', 'red onion', 'oregano', 'soy sauce', 'white vinegar', 'beef brisket', 'parsley', 'marjoram', 'balsamic vinegar', 'vegetable oil', 'fennel bulbs', 'cilantro', 'black pepper', 'worcestershire sauce', 'celery', 'kosher salt', 'mint', 'chili powder'}),
|
||||
('roast_leg_of_lamb_with_crispy_moroccan_carrots_&_couscous', 'Roast Leg of Lamb with Crispy Moroccan Carrots & Couscous', {'bay leaves', 'fresh thyme', 'carrot', 'tahini', 'garlic', 'baby carrot', 'oranges', 'filo pastry', 'tomatoes', 'harissa', 'onions', 'rosemary', 'leg of lamb', 'vegetable bullion', 'sesame seeds', 'honey', 'anchovy fillets', 'vinegar', 'olive oil', 'couscous', 'fresh mint', 'celery', 'yoghurt'}),
|
||||
('baked_chicken_jollof_rice', 'Baked Chicken Jollof Rice', {'coriander', 'salt', 'white pepper', 'water', 'bell pepper', 'turmeric', 'rice', 'scotch bonnet pepper', 'chicken', 'thyme', 'maggi cubes', 'garlic', 'ginger', 'onion', 'tomato', 'carrot', 'tomato puree'}),
|
||||
('seafood_risotto', 'Seafood Risotto', {'white wine', 'water', 'clams', 'crab legs', 'baby squid', 'lemon juice', 'parmesan cheese', 'olive oil', 'white onion', 'garlic', 'arborio risotto rice', 'fish stock', 'mussels', 'flat-leaf parsley', 'tomato paste', 'prawns', 'baby scallops', 'cherry tomatoes'}),
|
||||
('lamb_over_marinated_summer_squash_with_hazelnuts_and_ricotta', 'Lamb over Marinated Summer Squash with Hazelnuts and Ricotta', {'red pepper flakes', 'hazelnuts', 'zucchini', 'bay leaves', 'carrot', 'garlic', 'white wine vinegar', 'toasted bread', 'lemon', 'onions', 'rosemary', 'leg of lamb', 'summer squash', 'sea salt', 'anchovy fillets', 'vinegar', 'olive oil', 'black pepper', 'fresh mint', 'celery', 'kosher salt', 'fresh ricotta', 'sugar'})
|
||||
]
|
||||
|
||||
##############################
|
||||
# Data for test_check_drinks #
|
||||
##############################
|
||||
|
||||
|
||||
all_drinks = [('Amaretto Sour', ['almond liqueur', 'bourbon', 'cherries', 'egg white', 'lemon juice', 'lemon twist', 'simple syrup']),
|
||||
('Aperol Spritz', ['aperol', 'prosecco', 'soda water']),
|
||||
('Bannana Punch', ['banana', 'ginger ale', 'lemonade', 'orange juice', 'pineapple juice', 'sugar', 'water']),
|
||||
('Beet Sumac Soda', ['beet', 'club soda', 'fresh lemon juice', 'sugar', 'sumac']),
|
||||
('Better Than Celery Juice', ['apple cider vinegar', 'black pepper', 'celery stalks', 'club soda', 'granny smith apples', 'kosher salt', 'parsley']),
|
||||
('Black & Blue Berries', ['blackberries', 'blueberries', 'honey', 'lemon juice', 'soda water']),
|
||||
('Bloody Mary', ['celery', 'celery salt', 'lemon juice', 'pepper', 'tomato juice', 'vodka', 'worcestershire sauce']),
|
||||
('Bloody Shame', ['V8 juice', 'black pepper', 'celery', 'salt', 'tabasco sauce']),
|
||||
('Chai Blossom', ['chai tea bags', 'club soda', 'fresh lime juice', 'lemon twists', 'start anise pods', 'sugar']),
|
||||
('Chile-lime Pineapple Soda', ['chiles de árbol', 'club soda', 'fresh pineapple juice', 'kosher salt', 'lime juice', 'lime wedges', 'pink peppercorns', 'sugar']),
|
||||
('Citrus Fizz', ['organic marmalade cordial', 'seedlip grove 42', 'sparkling water']),
|
||||
('Dry Martini', ['dry vermouth', 'gin', 'lemon twist', 'olives']),
|
||||
('Espresso Martini', ['coffee liqueur', 'espresso', 'sugar syrup', 'vodka']),
|
||||
('Fermented Grape Soda', ['red seedless grapes', 'sugar', 'washed organic ginger']),
|
||||
('French 75', ['champagne', 'gin', 'lemon juice', 'sugar syrup']),
|
||||
('Gimlet', ['gin', 'lime juice', 'sugar syrup']),
|
||||
('Gin Fizz', ['gin', 'lemon juice', 'soda water', 'sugar syrup']),
|
||||
('Huckleberry Shrub', ['club soda', 'huckleberries', 'sugar', 'white wine vinegar']),
|
||||
('Mai Tai', ['cherries', 'lime juice', 'lime wedge', 'mint leaves', 'orange curacao', 'orgeat syrup', 'rum', 'sugar syrup']),
|
||||
('Mango Mule', ['cucumber', 'fresh lime juice', 'ginger beer', 'honey syrup', 'ice', 'mango puree']),
|
||||
('Manhattan', ['bitters', 'cherry', 'rye', 'sweet vermouth']),
|
||||
('Maple-Ginger Cider Switchel', ['apple cider vinegar', 'club soda', 'fresh ginger', 'fresh lime juice', 'maple syrup', 'mint sprigs']),
|
||||
('Margarita', ['lime juice', 'salt', 'simple syrup', 'tequila', 'triple sec']),
|
||||
('Mojito', ['lime', 'mint leaves', 'soda water', 'sugar syrup', 'white rum']),
|
||||
('Moscow Mule', ['ginger beer', 'lime', 'lime juice', 'vodka']),
|
||||
('Negroni', ['bitters', 'gin', 'sweet vermouth']),
|
||||
('Old Fashioned', ['bitters', 'bourbon', 'orange juice', 'orange slices', 'sugar']),
|
||||
('PG13 Singapore Sling', ['fresh lime juice', 'mango juice', 'mint sprigs', 'pineapple juice', 'pomegranate juice', 'tonic water']),
|
||||
('Penicillin', ['ginger', 'honey simple syrup', 'lemon juice', 'scotch']),
|
||||
('Pina Colada', ['cherries', 'coconut milk', 'cream of coconut', 'dark rum', 'fresh pineapple', 'lime juice', 'white rum']),
|
||||
('Raspberry Almond Soda', ['almonds', 'club soda', 'kosher salt', 'limes', 'ripe raspberries', 'sugar']),
|
||||
('Salted Meyer Lemon and Sage Presse', ['club soda meyer lemons', 'kosher salt', 'sage leaves', 'simple syrup']),
|
||||
('Salted Watermelon Juice', ['cubed watermelon', 'kosher salt', 'lime wedges']),
|
||||
('Shirley Tonic', ['cinnamon sticks', 'club soda', 'ginger', 'lemon twists', 'pomegranate juice', 'sugar', 'whole cloves']),
|
||||
('Shirley ginger', ['brooklyn crafted lemon lime ginger beer', 'club soda', 'grenadine', 'lime juice']),
|
||||
('Spiced Hibiscus Tea', ['cinnamon sticks', 'dried hibiscus flowers', 'ginger', 'honey', 'lemon juice', 'lemon wheels', 'whole allspice']),
|
||||
('Turmeric Tonic', ['agave syrup', 'cayenne pepper', 'lemon', 'peeled ginger', 'peeled turmeric', 'sparkling water']),
|
||||
('Virgin Cucumber Gimlet', [';ime juice', 'club soda', 'muddled cucumber', 'simple syrup']),
|
||||
('Whiskey Sour', ['cherry', 'lemon juice', 'lemon slices', 'superfine sugar', 'whiskey'])
|
||||
]
|
||||
|
||||
drink_names = ['Amaretto Sour Cocktail','Aperol Spritz Cocktail','Bannana Punch Mocktail','Beet Sumac Soda Mocktail',
|
||||
'Better Than Celery Juice Mocktail','Black & Blue Berries Mocktail','Bloody Mary Cocktail',
|
||||
'Bloody Shame Mocktail','Chai Blossom Mocktail','Chile-lime Pineapple Soda Mocktail',
|
||||
'Citrus Fizz Mocktail','Dry Martini Cocktail','Espresso Martini Cocktail','Fermented Grape Soda Mocktail',
|
||||
'French 75 Cocktail','Gimlet Cocktail','Gin Fizz Cocktail','Huckleberry Shrub Mocktail',
|
||||
'Mai Tai Cocktail','Mango Mule Mocktail','Manhattan Cocktail','Maple-Ginger Cider Switchel Mocktail',
|
||||
'Margarita Cocktail','Mojito Cocktail','Moscow Mule Cocktail','Negroni Cocktail',
|
||||
'Old Fashioned Cocktail','PG13 Singapore Sling Mocktail','Penicillin Cocktail','Pina Colada Cocktail',
|
||||
'Raspberry Almond Soda Mocktail','Salted Meyer Lemon and Sage Presse Mocktail',
|
||||
'Salted Watermelon Juice Mocktail','Shirley Tonic Mocktail','Shirley ginger Mocktail',
|
||||
'Spiced Hibiscus Tea Mocktail','Turmeric Tonic Mocktail','Virgin Cucumber Gimlet Mocktail',
|
||||
'Whiskey Sour Cocktail']
|
||||
|
||||
|
||||
#################################
|
||||
# Data for test_categorize_dish #
|
||||
#################################
|
||||
|
||||
all_dishes = recipes_without_duplicates
|
||||
|
||||
dishes_categorized = ['Zucchini Fritters with Lemon-Thyme Coconut Yogurt: PALEO',
|
||||
'Winter Cobb Salad: VEGETARIAN',
|
||||
'White Cheddar Scalloped Potatoes: VEGETARIAN',
|
||||
'Walnut Ravioli with Artichokes and Tomatoes: VEGETARIAN',
|
||||
'Waakye: VEGAN',
|
||||
'Vegetarian Khoresh Bademjan: VEGAN',
|
||||
'Vegan Pizza with Caramelized Onions: VEGAN',
|
||||
'Vegan Mini Savory Mince Pies: VEGAN',
|
||||
'Vegan Carbonara: VEGAN',
|
||||
'Sweet and Spicy Crispy Green Beans: VEGAN',
|
||||
'Summer Minestrone Cups: VEGETARIAN',
|
||||
'Sticky Lemon Tofu: VEGAN',
|
||||
'Shrimp, Bacon and Crispy Chickpea Tacos with Salsa de Guacamole: OMNIVORE',
|
||||
'Shakshuka: VEGAN',
|
||||
'Seared Salmon with Pickled Vegetable and Watercress Salad: KETO',
|
||||
'Seafood Risotto: OMNIVORE',
|
||||
'Satay Steak Skewers: KETO',
|
||||
'Roasted Corn and Zucchini Salad: VEGAN',
|
||||
'Roasted Chicken with Roasted Tomatoes, Avocado, and Sweet Potatoes: PALEO',
|
||||
'Roast Pumpkin and Lentil Salad with Roasted Lemon Dressing: VEGETARIAN',
|
||||
'Roast Leg of Lamb with Crispy Moroccan Carrots & Couscous: OMNIVORE',
|
||||
'Pumpkin Bread Crostini: PALEO',
|
||||
'Prawn and Herb Omelette: KETO',
|
||||
'Pork Chops with Grilled Castelfranco Radicchio and Asparagus: KETO',
|
||||
'Parmesan Crackers and Spinach Crackers with Salmon Pate: KETO',
|
||||
'Nut Wellington: VEGETARIAN',
|
||||
'Mushroom Lasagna: VEGETARIAN',
|
||||
'Lamb over Marinated Summer Squash with Hazelnuts and Ricotta: OMNIVORE',
|
||||
'Kisir with Warm Pita: VEGAN',
|
||||
'Kingfish Lettuce Cups: KETO',
|
||||
'Grilled Tofu Tacos: VEGETARIAN',
|
||||
'Grilled Shrimp and Pesto over Zucchini Noodles: PALEO',
|
||||
'Grilled Pork Chops with Mango Pineapple Salsa: PALEO',
|
||||
'Grilled Flank Steak with Caesar Salad: PALEO',
|
||||
'Grilled Fish Tacos with Cauliflower Tortillas: PALEO',
|
||||
'Governor Shrimp Tacos: OMNIVORE',
|
||||
'Golden Potato Salad: VEGAN',
|
||||
'Georgian Eggplant Rolls with Walnuts: VEGAN',
|
||||
'Fried Zucchini with Balsamic and Chili Dressing: VEGETARIAN',
|
||||
'Fresh Garden Peas over Cauliflower Almond Puree: VEGETARIAN',
|
||||
'Flank Steak with Chimichurri and Asparagus: KETO',
|
||||
'Dahi Puri with Black Chickpeas: OMNIVORE',
|
||||
'Chicken with Tamarind and Apricot Sauce: PALEO',
|
||||
'Cherry Tomato, Watercress and Fava Bean Salad: VEGETARIAN',
|
||||
'Cheela with Spicy Mango Chutney: VEGAN',
|
||||
'Celeriac Schnitzel: VEGAN',
|
||||
'Cauliflower Pizza with Roasted Tomatoes and Chicken: KETO',
|
||||
'Carrot Puff Pastry Tart: VEGAN',
|
||||
'Cambodian-Style Vegetable Spring Rolls: VEGETARIAN',
|
||||
'Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney: KETO',
|
||||
'Burnt Masala Wings with Classic Coleslaw: OMNIVORE',
|
||||
'Burmese Tofu with Garlic, Ginger and Chili Sauce: VEGAN',
|
||||
'Brisket with Grilled Rhubarb, Onions, and Fennel: OMNIVORE',
|
||||
'Braised Pork Belly with Apple Salad: KETO',
|
||||
'BLT Bites: PALEO',
|
||||
'Beachside Snapper: OMNIVORE',
|
||||
'Barley Risotto: VEGETARIAN',
|
||||
'Baked Kelewele: VEGAN',
|
||||
'Baked Chicken Jollof Rice: OMNIVORE',
|
||||
'Avocado Deviled Eggs: PALEO',
|
||||
'Asparagus Puffs: VEGETARIAN']
|
||||
|
||||
|
||||
#########################################
|
||||
# Data for test_tag_special_ingredients #
|
||||
#########################################
|
||||
|
||||
|
||||
dupes = ['Baked Kelewele', 'Barley Risotto', 'Burmese Tofu with Garlic, Ginger and Chili Sauce',
|
||||
'Cambodian-Style Vegetable Spring Rolls', 'Fried Zucchini with Balsamic and Chili Dressing',
|
||||
'Kisir with Warm Pita', 'Shakshuka', 'Summer Minestrone Cups', 'Vegetarian Khoresh Bademjan']
|
||||
|
||||
nondupes = ['Brisket with Grilled Rhubarb, Onions, and Fennel', 'Burnt Masala Wings with Classic Coleslaw',
|
||||
'Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney',
|
||||
'Cauliflower Pizza with Roasted Tomatoes and Chicken', 'Dahi Puri with Black Chickpeas',
|
||||
'Flank Steak with Chimichurri and Asparagus', 'Kingfish Lettuce Cups',
|
||||
'Shrimp, Bacon and Crispy Chickpea Tacos with Salsa de Guacamole']
|
||||
|
||||
group_1 = [item for item in recipes_with_duplicates if item[0] in dupes]
|
||||
group_2 = [(item[1], item[2]) for item in recipes_without_duplicates if item[1] in nondupes]
|
||||
|
||||
dishes_to_special_label = sorted(group_1 + group_2)
|
||||
|
||||
dishes_labeled = [('Baked Kelewele', {'red onion'}),
|
||||
('Barley Risotto', {'garlic', 'red onion', 'parmesan cheese'}),
|
||||
('Brisket with Grilled Rhubarb, Onions, and Fennel', {'soy sauce', 'garlic', 'red onion', 'yellow onion'}),
|
||||
('Burmese Tofu with Garlic, Ginger and Chili Sauce', {'soy sauce', 'garlic', 'peanuts'}),
|
||||
('Burnt Masala Wings with Classic Coleslaw', {'honey', 'butter', 'garlic cloves'}),
|
||||
('Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney', {'heavy cream', 'garlic', 'butter', 'tomatoes'}),
|
||||
('Cambodian-Style Vegetable Spring Rolls', {'soy sauce', 'firm tofu', 'garlic', 'toasted peanuts'}),
|
||||
('Cauliflower Pizza with Roasted Tomatoes and Chicken', {'parmesan', 'mozzarella cheese', 'tomato paste', 'cherry tomatoes', 'eggs'}),
|
||||
('Dahi Puri with Black Chickpeas', {'red onion', 'whole-milk yogurt'}),
|
||||
('Flank Steak with Chimichurri and Asparagus', {'garlic'}),
|
||||
('Fried Zucchini with Balsamic and Chili Dressing', {'honey', 'garlic', 'red onion'}),
|
||||
('Kingfish Lettuce Cups', {'soy sauce', 'oyster sauce', 'garlic', 'grilled king fish'}),
|
||||
('Kisir with Warm Pita', {'bulgur', 'tomato paste'}),
|
||||
('Shakshuka', {'tomatoes', 'tomato paste', 'firm tofu', 'yellow onion'}),
|
||||
('Shrimp, Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', {'shrimp', 'garlic', 'butter', 'slivered almonds', 'bacon'}),
|
||||
('Summer Minestrone Cups', {'garlic', 'tomatoes', 'parmesan cheese'}),
|
||||
('Vegetarian Khoresh Bademjan', {'slivered almonds', 'tomato paste'})]
|
||||
|
||||
|
||||
#####################################
|
||||
# Data for test_compile_ingredients #
|
||||
#####################################
|
||||
|
||||
|
||||
ingredients_only = [[{'bulgur', 'pomegranate molasses', 'chopped parsley', 'lemon juice', 'tomato', 'persian cucumber', 'tomato paste', 'spring onion', 'water', 'olive oil'},
|
||||
{'vegan unsweetened yoghurt', 'yellow onion', 'firm tofu', 'smoked paprika', 'tomatoes', 'tomato paste', 'sugar', 'cloves', 'cumin', "za'atar", 'olive oil', 'harissa', 'red bell pepper'},
|
||||
{'yellow split peas', 'tomato paste', 'black pepper', 'pomegranate concentrate', 'yellow onions', 'slivered almonds', 'ground turmeric', 'barberries', 'basmati rice', 'lemon juice', 'hot water', 'cayenne pepper', 'chinese eggplants', 'salt', 'orange juice', 'saffron powder', 'vegan butter', 'orange zest', 'kosher salt'},
|
||||
{'smoked paprika', 'black peppercorn', 'red onion', 'grains of selim', 'cayenne pepper', 'calabash nutmeg', 'coconut oil', 'cloves', 'fresh ginger', 'salt', 'ripe plantains'},
|
||||
{'baking soda', 'sorghum stems', 'coconut oil', 'black-eyed peas', 'water', 'salt', 'white rice'},
|
||||
{'pomegranate seeds', 'oil', 'coriander', 'garlic', 'khmeli suneli', 'eggplants', 'black pepper', 'vinegar', 'walnuts', 'water', 'salt'},
|
||||
{'soy sauce', 'oil', 'chili flakes', 'garlic', 'brown sugar', 'ginger', 'peanuts', 'rice vinegar', 'spring onions', 'water', 'turmeric', 'salt', 'chickpea flour'},
|
||||
{'soy sauce', 'parsley', 'lemon', 'sunflower oil', 'black pepper', 'celeriac', 'breadcrumbs', 'water', 'salt', 'flour', 'chickpea flour'},
|
||||
{'soy sauce', 'vegetable stock', 'tofu', 'cornstarch', 'lemon juice', 'lemon zest', 'garlic', 'ginger', 'black pepper', 'sugar', 'water', 'salt', 'vegetable oil'},
|
||||
{'soy sauce', 'smoked tofu', 'lemon juice', 'nutritional yeast', 'mixed herbs', 'garlic', 'black pepper', 'silken tofu', 'turmeric', 'salt', 'olive oil', 'spaghetti'},
|
||||
{'mushrooms', 'rosemary', 'garlic', 'red pepper flakes', 'yeast', 'barley malt', 'water', 'olive oil', 'garlic powder', 'oregano', 'honey', 'nutritional yeast', 'red onion', 'tomatoes', 'cashews', 'sugar', 'bell pepper', 'flour', 'salt', 'fresh basil'},
|
||||
{'clove powder', 'oil', 'cinnamon powder', 'nigella seeds', 'curry leaves', 'coriander seeds', 'garlic', 'mangoes', 'mashed potatoes', 'cardamom powder', 'vinegar', 'water', 'mustard seeds', 'coriander powder', 'cumin powder', 'mango powder', 'garam masala', 'red chili powder', 'hing', 'garlic paste', 'turmeric powder', 'cilantro', 'sugar', 'onion', 'serrano chili', 'fresh ginger', 'turmeric', 'salt', 'fresh red chili', 'chickpea flour'},
|
||||
{'soy sauce', 'pomegranate molasses', 'sesame oil', 'green beans', 'sunflower oil', 'scallions', 'garlic', 'carrot', 'ginger', 'sesame seeds', 'tomato paste', 'bell pepper', 'siracha'},
|
||||
{'mushrooms', 'cinnamon powder', 'rosemary', 'corn flour', 'ginger', 'brown sugar', 'carrot', 'black pepper', 'raisins', 'butternut squash', 'vegetarian worcestershire sauce', 'parev shortcrust pastry', 'olive oil', 'vegetable stock', 'dried cherries', 'lemon juice', 'lemon zest', 'figs', 'dried cranberries', 'apples', 'pecans', 'onion', 'orange juice', 'currants', 'dried blueberries', 'salt', 'brandy', 'orange zest', 'allspice powder'},
|
||||
{'green onions', 'lemon juice', 'lemon zest', 'dill', 'corn', 'tomatoes', 'black pepper', 'zucchini', 'olive oil'},
|
||||
{'mustard seeds', 'cumin seeds', 'lemon juice', 'garlic', 'black pepper', 'balsamic vinegar', 'yukon gold potato', 'chives', 'turmeric', 'salt', 'olive oil'},
|
||||
{'olive oil', 'lemon', 'lemon juice', 'pareve puff pastry', 'brown sugar', 'red onion', 'carrot', 'garlic', 'black pepper', 'thyme', 'vegan butter', 'water', 'salt', 'ground almonds'}
|
||||
],
|
||||
|
||||
[{'nutmeg', 'garlic', 'black pepper', 'onions', 'butter', 'parmesan cheese', 'portobello mushrooms', 'flour', 'dried lasagna noodles', 'olive oil', 'milk', 'kosher salt'},
|
||||
{'sage', 'puff pastry sheets', 'hazelnuts', 'almonds', 'black pepper', 'thyme', 'marmite', 'breadcrumbs', 'walnuts', 'dates', 'eggs', 'olive oil', 'brazil nuts', 'leeks', 'chestnuts', 'cashews', 'apples', 'pecans', 'butter', 'salt'},
|
||||
{'shallots', 'garlic', 'cream', 'thyme', 'breadcrumbs', 'sharp white cheddar', 'yukon gold potato', 'milk', 'kosher salt'},
|
||||
{'smoked tofu', 'shiitake mushrooms', 'red wine vinegar', 'garlic', 'tomatoes', 'black pepper', 'avocados', 'blue cheese', 'onion', 'lacinato kale', 'eggs', 'olive oil'},
|
||||
{'honey', 'lemon', 'dijon mustard', 'feta cheese', 'red wine vinegar', 'red onion', 'watercress', 'green lentils', 'currants', 'capers', 'pepitas', 'fresh pumpkin', 'olive oil'},
|
||||
{'lime juice', 'toasted peanuts', 'firm tofu', 'garlic', 'corn flour', 'bean sprouts', 'carrot', 'palm sugar', 'shallots', 'red chili', 'vermicelli noodles', 'wombok', 'soy sauce', 'sunflower oil', 'spring roll wrappers', 'vegetarian fish sauce'},
|
||||
{'fresh peas', 'rosemary', 'garlic', 'chickpeas', 'carrot', 'leek', 'green lentils', 'red chili', 'olive oil', 'croutons', 'fresh corn', 'lemon zest', 'green beans', 'parmesan rind', 'basil', 'tomatoes', 'vegetable bullion', 'parmesan cheese', 'celery', 'mint'},
|
||||
{'honey', 'lemon juice', 'garlic', 'red onion', 'red thai chili', 'pomegranate', 'balsamic vinegar', 'mint leaves', 'zucchini', 'olive oil'},
|
||||
{'sage', 'beets', 'pearl barley', 'brussel sprouts', 'rosemary', 'garlic', 'carrot', 'red onion', 'black pepper', 'thyme', 'butternut squash', 'vegetable bullion', 'parmesan cheese', 'olive oil', 'white wine'},
|
||||
{'fresh peas', 'fresh cherry tomatoes', 'garlic', 'watercress', 'balsamic vinegar', 'fresh or frozen fava beans', 'fresh cherry bocconcini', 'salt', 'olive oil'},
|
||||
{'red onions', 'lemon', 'fresh peas', 'garlic', 'grated nutmeg', 'cream', 'cauliflower', 'blanched almonds', 'fresh pea tendrils', 'olive oil', 'vegetable oil'},
|
||||
{'pine nuts', 'oil marinated artichokes', 'olives', 'rosemary', 'fresh tomatoes', 'ricotta cheese', 'black pepper', 'fresh artichoke hearts', 'walnuts', 'pasta sheets', 'lemon juice', 'lemon zest', 'basil', 'red onion', 'butter', 'salt'},
|
||||
{'egg', 'asparagus', 'lemon juice', 'red onion', 'ricotta cheese', 'black pepper', 'thyme', 'salt', 'parmesan cheese', 'puff pastry', 'chives'},
|
||||
{'cotija cheese', 'masa', 'fresh cilantro leaves', 'jalapeño chili', 'chipotle chili', 'firm tofu', 'garlic', 'carrot', 'roasted corn', 'tomatillos', 'pepitas', 'ancho chili', 'crema', 'red onions', 'pasilla chili', 'lemon', 'hot water', 'lemon juice', 'tomatoes', 'avocado', 'cumin', 'red cabbage', 'limes', 'black beans'}
|
||||
],
|
||||
|
||||
[{'baking soda', 'coconut flour', 'lemon juice', 'lemon zest', 'eggs', 'coconut yogurt', 'black pepper', 'fresh thai chili', 'coconut oil', 'chives', 'zucchini', 'salt'},
|
||||
{'lime juice', 'fresh cilantro leaves', 'eggs', 'seranno chili', 'avocado', 'pepitas', 'salt', 'garlic powder'},
|
||||
{'pine nuts', 'white vinegar', 'chipotle chili', 'scallions', 'garlic', 'paleo parmesan cheese', 'black pepper', 'avocado oil', 'treviso', 'olive oil', 'radishes', 'flank steak', 'dijon mustard', 'castelfranco radicchio', 'worcestershire sauce', 'fresh parsley', 'cherry tomatoes', 'salt', 'avocado mayonnaise'},
|
||||
{'coconut flour', 'garlic', 'black pepper', 'cloves', 'eggs', 'olive oil', 'onions', 'honey', 'apple cider vinegar', 'almond butter', 'baking soda', 'nutmeg', 'pumpkin puree', 'cinnamon', 'shrimp', 'basil', 'coconut oil', 'cherry tomatoes', 'salt', 'fresh red chili'},
|
||||
{'mustard seed', 'onion', 'kale', 'cherry tomatoes', 'bacon', 'paleo mayonnaise'},
|
||||
{'purple sweet potato', 'chiles de árbol', 'garlic', 'tomatoes', 'safflower oil', 'black pepper', 'mexican oregano', 'avocados', 'shallots', 'cumin', 'olive oil', 'lemons', 'whole chicken', 'limes', 'kosher salt'},
|
||||
{'garlic', 'black pepper', 'dried apricots', 'roma tomatoes', 'water', 'olive oil', 'honey', 'cinnamon', 'ground cumin', 'cider vinegar', 'chili powder', 'safflower oil', 'homemade tamarind concentrate', 'white chicken', 'allspice', 'chipotles', 'salt', 'homemade apricot honey preserves', 'kosher salt'},
|
||||
{'green onions', 'serrano chili', 'shredded red cabbage', 'smoked paprika', 'mango', 'eggs', 'black pepper', 'cilantro', 'cauliflower', 'avocado', 'lime', 'cumin', 'salt', 'tilapia'},
|
||||
{'cilantro leaves', 'lime', 'pineapple', 'chipotle chili', 'garlic', 'mangoes', 'cauliflower', 'avocado', 'serrano chili', 'pork chops', 'lime zest', 'lacinato kale', 'onions'},
|
||||
{'pine nuts', 'shrimp', 'green bell pepper', 'lemon juice', 'basil', 'lemon zest', 'garlic', 'tomatoes', 'cashews', 'yellow bell pepper', 'cumin', 'zucchini', 'salt', 'olive oil', 'red bell pepper'}
|
||||
],
|
||||
|
||||
[{'parmesan', 'almond meal', 'rosemary', 'mozzarella cheese', 'roasted chicken', 'tomato paste', 'cauliflower', 'cherry tomatoes', 'eggs', 'fresh basil', 'olive oil'},
|
||||
{'white vinegar', 'asparagus', 'flank steak', 'chipotle chili', 'scallions', 'garlic', 'black pepper', 'cauliflower', 'sour cream', 'fresh parsley', 'salt', 'olive oil'},
|
||||
{'soy sauce', 'watermelon radishes', 'lime juice', 'fish sauce', 'mirin', 'little gem lettuce heads', 'peanut oil', 'garlic', 'sesame seeds', 'oyster sauce', 'spring onions', 'avocado', 'grilled king fish', 'red cabbage'},
|
||||
{'clove powder', 'curry leaves', 'coriander seeds', 'ginger', 'cardamom powder', 'vinegar', 'mustard seeds', 'mango powder', 'garam masala', 'red chili powder', 'chicken', 'hing', 'turmeric powder', 'tomatoes', 'ginger garlic paste', 'fresh greek yogurt', 'fresh ginger', 'monk fruit', 'turmeric', 'cashew nuts', 'salt', 'fresh red chili', 'cinnamon powder', 'nigella seeds', 'whole small crimini mushrooms', 'brussel sprouts', 'garlic', 'mangoes', 'heavy cream', 'cloves', 'coriander powder', 'cumin powder', 'cardamom', 'green chili', 'cinnamon', 'garlic paste', 'red chili flakes', 'lemon juice', 'cilantro', 'butter', 'dried fenugreek leaves', 'boned chicken'},
|
||||
{'green onions', 'parsley', 'sesame seeds', 'black pepper', 'chives', 'eggs', 'fennel bulb', 'tahini', 'olive oil', 'harissa', 'shrimp', 'lemon juice', 'dill', 'butter', 'onion', 'fresh cucumber', 'monk fruit', 'salt'},
|
||||
{'sriacha', 'apple cider vinegar', 'lime juice', 'fish sauce', 'red and green thai chili', 'flank steak', 'carrot', 'peanuts', 'cucumbers', 'roasted peanuts', 'crunchy peanut butter', 'kecap manis', 'monk fruit', 'micro cilantro', 'olive oil'},
|
||||
{'spinach', 'parmesan cheese', 'coconut flour', 'chili flakes', 'garlic', 'black pepper', 'cream cheese', 'ghee', 'lemon juice', 'flaxmeal', 'red onion', 'salt', 'salmon fillets', 'pecans', 'almond flour', 'cumin', 'fresh cucumber', 'cherry tomatoes', 'chives', 'avocado mayonnaise'},
|
||||
{'rosemary', 'garlic', 'black pepper', 'thyme', 'avocado oil', 'eggs', 'oregano', 'asparagus', 'lemon', 'dijon mustard', 'basil', 'castelfranco radicchio', 'dill', 'butter', 'pork chops', 'monk fruit', 'salt', 'avocado mayonnaise'},
|
||||
{'lime juice', 'caster sugar', 'toasted buckwheat', 'lemon juice', 'red wine vinegar', 'red onion', 'dutch carrot', 'salmon steaks', 'watercress', 'pink peppercorns', 'shallots', 'fennel seeds', 'red cabbage', 'radishes'},
|
||||
{'cilantro leaves', 'green cabbage', 'lemon juice', 'pork belly', 'dark soy sauce', 'granny smith apples', 'ginger', 'light soy sauce', 'sesame seeds', 'black pepper', 'cinnamon sticks', 'spring onions', 'star anise', 'monk fruit', 'olive oil'}
|
||||
],
|
||||
|
||||
[{'lime juice', 'salsa', 'green bell pepper', 'anaheim chili', 'black pepper', 'olive oil', 'yellow mustard', 'red bell pepper', 'soy sauce', 'mexican crema', 'mayonnaise', 'white onion', 'garlic cloves', 'fresh corn tortillas', 'red onion', 'tomatoes', 'limes', 'worcestershire sauce', 'butter', 'yellow bell pepper', 'salt', 'red snapper'},
|
||||
{'lime juice', 'poblano chili', 'tomato paste', 'black pepper', 'chipotle adobo sauce', 'chile manzano', 'roma tomatoes', 'pepitas', 'oaxaca cheese', 'white onion', 'shelled large shrimp', 'garlic cloves', 'fresh corn tortillas', 'red onion', 'worcestershire sauce', 'avocado', 'butter', 'kosher salt'},
|
||||
{'fresh tortillas', 'shrimp', 'garlic', 'chickpeas', 'black pepper', 'slivered almonds', 'guajillo chile', 'bacon', 'sea salt', 'butter', 'onion', 'avocado', 'olive oil'},
|
||||
{'lime juice', 'green cabbage', 'chiles de árbol', 'green cardamom', 'ginger', 'carrot', 'sriracha', 'black pepper', 'cinnamon sticks', 'celery seeds', 'cloves', 'black cardamom', 'olive oil', 'chicken wings', 'apple cider vinegar', 'mayonnaise', 'honey', 'black peppercorns', 'lemon juice', 'garlic cloves', 'tamarind concentrate', 'cilantro', 'butter', 'serrano chili', 'red cabbage', 'kosher salt'},
|
||||
{'whole-milk yogurt', 'chaat masala', 'ginger', 'pani puri', 'scallion chutney', 'yukon gold potato', 'black chickpeas', 'thin sev', 'date syrup', 'red onion', 'roasted chicken', 'chili powder', 'tamarind concentrate', 'cumin', 'turmeric', 'mint'},
|
||||
{'white vinegar', 'parsley', 'yellow onion', 'rhubarb', 'garlic', 'brown sugar', 'black pepper', 'thyme', 'crushed red pepper flakes', 'fennel bulbs', 'beer', 'marjoram', 'vegetable oil', 'soy sauce', 'oregano', 'lemon', 'red onion', 'chili powder', 'cilantro', 'beef brisket', 'balsamic vinegar', 'worcestershire sauce', 'celery', 'mint', 'kosher salt'},
|
||||
{'oranges', 'rosemary', 'garlic', 'anchovy fillets', 'couscous', 'carrot', 'sesame seeds', 'fresh thyme', 'vinegar', 'olive oil', 'tahini', 'harissa', 'onions', 'honey', 'fresh mint', 'leg of lamb', 'tomatoes', 'baby carrot', 'vegetable bullion', 'filo pastry', 'celery', 'bay leaves', 'yoghurt'},
|
||||
{'tomato puree', 'garlic', 'ginger', 'tomato', 'carrot', 'thyme', 'white pepper', 'water', 'maggi cubes', 'chicken', 'rice', 'coriander', 'scotch bonnet pepper', 'bell pepper', 'onion', 'turmeric', 'salt'},
|
||||
{'garlic', 'tomato paste', 'baby scallops', 'mussels', 'water', 'crab legs', 'olive oil', 'baby squid', 'fish stock', 'lemon juice', 'white onion', 'arborio risotto rice', 'clams', 'parmesan cheese', 'flat-leaf parsley', 'cherry tomatoes', 'prawns', 'white wine'},
|
||||
{'toasted bread', 'rosemary', 'hazelnuts', 'anchovy fillets', 'garlic', 'carrot', 'summer squash', 'black pepper', 'red pepper flakes', 'vinegar', 'sea salt', 'zucchini', 'olive oil', 'onions', 'white wine vinegar', 'fresh mint', 'leg of lamb', 'lemon', 'fresh ricotta', 'sugar', 'celery', 'bay leaves', 'kosher salt'}
|
||||
]]
|
||||
|
||||
|
||||
#####################################
|
||||
# Data for test_separate_appetizers #
|
||||
#####################################
|
||||
|
||||
|
||||
vegan = ['Kisir with Warm Pita', 'Shakshuka', 'Vegetarian Khoresh Bademjan', 'Baked Kelewele', 'Waakye', 'Georgian Eggplant Rolls with Walnuts', 'Burmese Tofu with Garlic, Ginger and Chili Sauce', 'Celeriac Schnitzel', 'Sticky Lemon Tofu', 'Vegan Carbonara', 'Vegan Pizza with Caramelized Onions', 'Cheela with Spicy Mango Chutney', 'Sweet and Spicy Crispy Green Beans', 'Vegan Mini Savory Mince Pies', 'Roasted Corn and Zucchini Salad', 'Golden Potato Salad', 'Carrot Puff Pastry Tart', 'Kisir with Warm Pita', 'Baked Kelewele', 'Burmese Tofu with Garlic, Ginger and Chili Sauce', 'Vegan Carbonara', 'Sweet and Spicy Crispy Green Beans', 'Golden Potato Salad']
|
||||
vegan_appetizers = ['Georgian Eggplant Rolls with Walnuts', 'Cheela with Spicy Mango Chutney', 'Vegan Mini Savory Mince Pies', 'Carrot Puff Pastry Tart', 'Georgian Eggplant Rolls with Walnuts', 'Carrot Puff Pastry Tart']
|
||||
vegan_dishes = ['Golden Potato Salad', 'Roasted Corn and Zucchini Salad', 'Sticky Lemon Tofu', 'Shakshuka', 'Burmese Tofu with Garlic, Ginger and Chili Sauce', 'Vegan Pizza with Caramelized Onions', 'Celeriac Schnitzel', 'Waakye', 'Vegan Carbonara', 'Sweet and Spicy Crispy Green Beans', 'Vegetarian Khoresh Bademjan', 'Baked Kelewele', 'Kisir with Warm Pita']
|
||||
vegan_appetizer_names = ['Georgian Eggplant Rolls with Walnuts','Cheela with Spicy Mango Chutney','Vegan Mini Savory Mince Pies','Carrot Puff Pastry Tart']
|
||||
|
||||
|
||||
vegetarian = ['Mushroom Lasagna', 'Nut Wellington', 'Roast Pumpkin and Lentil Salad with Roasted Lemon Dressing', 'Cambodian-Style Vegetable Spring Rolls', 'Summer Minestrone Cups', 'Fried Zucchini with Balsamic and Chili Dressing', 'Barley Risotto', 'Cherry Tomato, Watercress and Fava Bean Salad', 'Fresh Garden Peas over Cauliflower Almond Puree', 'Walnut Ravioli with Artichokes and Tomatoes', 'Asparagus Puffs', 'Grilled Tofu Tacos', 'Mushroom Lasagna', 'Cambodian-Style Vegetable Spring Rolls', 'Barley Risotto', 'Walnut Ravioli with Artichokes and Tomatoes']
|
||||
vegetarian_appetizers = ['Cambodian-Style Vegetable Spring Rolls', 'Summer Minestrone Cups', 'Asparagus Puffs', 'Grilled Tofu Tacos', 'Cambodian-Style Vegetable Spring Rolls', 'Grilled Tofu Tacos']
|
||||
vegetarian_dishes = ['Walnut Ravioli with Artichokes and Tomatoes', 'Mushroom Lasagna', 'Fried Zucchini with Balsamic and Chili Dressing', 'Barley Risotto', 'Nut Wellington', 'Cherry Tomato, Watercress and Fava Bean Salad', 'Roast Pumpkin and Lentil Salad with Roasted Lemon Dressing', 'Fresh Garden Peas over Cauliflower Almond Puree']
|
||||
vegetarian_appetizer_names = ['Cambodian-Style Vegetable Spring Rolls','Summer Minestrone Cups','Asparagus Puffs','Grilled Tofu Tacos']
|
||||
|
||||
|
||||
paleo = ['Zucchini Fritters with Lemon-Thyme Coconut Yogurt', 'Avocado Deviled Eggs', 'Grilled Flank Steak with Caesar Salad', 'Pumpkin Bread Crostini', 'BLT Bites', 'Roasted Chicken with Roasted Tomatoes, Avocado, and Sweet Potatoes', 'Chicken with Tamarind and Apricot Sauce', 'Grilled Fish Tacos with Cauliflower Tortillas', 'Grilled Pork Chops with Mango Pineapple Salsa', 'Grilled Shrimp and Pesto over Zucchini Noodles', 'Zucchini Fritters with Lemon-Thyme Coconut Yogurt', 'Pumpkin Bread Crostini', 'Chicken with Tamarind and Apricot Sauce', 'Grilled Shrimp and Pesto over Zucchini Noodles']
|
||||
paleo_appetizers = ['Zucchini Fritters with Lemon-Thyme Coconut Yogurt', 'Avocado Deviled Eggs', 'Pumpkin Bread Crostini', 'BLT Bites', 'Zucchini Fritters with Lemon-Thyme Coconut Yogurt', 'BLT Bites']
|
||||
paleo_dishes = ['Roasted Chicken with Roasted Tomatoes, Avocado, and Sweet Potatoes', 'Grilled Flank Steak with Caesar Salad', 'Grilled Fish Tacos with Cauliflower Tortillas', 'Grilled Pork Chops with Mango Pineapple Salsa', 'Grilled Shrimp and Pesto over Zucchini Noodles', 'Chicken with Tamarind and Apricot Sauce']
|
||||
paleo_appetizer_names = ['Zucchini Fritters with Lemon-Thyme Coconut Yogurt','Avocado Deviled Eggs','Pumpkin Bread Crostini','BLT Bites']
|
||||
|
||||
|
||||
keto = ['Cauliflower Pizza with Roasted Tomatoes and Chicken', 'Flank Steak with Chimichurri and Asparagus', 'Kingfish Lettuce Cups', 'Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney', 'Prawn and Herb Omelette', 'Satay Steak Skewers', 'Parmesan Crackers and Spinach Crackers with Salmon Pate', 'Pork Chops with Grilled Castelfranco Radicchio and Asparagus', 'Seared Salmon with Pickled Vegetable and Watercress Salad', 'Braised Pork Belly with Apple Salad', 'Cauliflower Pizza with Roasted Tomatoes and Chicken', 'Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney', 'Parmesan Crackers and Spinach Crackers with Salmon Pate', 'Braised Pork Belly with Apple Salad']
|
||||
keto_appetizers = ['Kingfish Lettuce Cups', 'Prawn and Herb Omelette', 'Satay Steak Skewers', 'Parmesan Crackers and Spinach Crackers with Salmon Pate', 'Kingfish Lettuce Cups', 'Parmesan Crackers and Spinach Crackers with Salmon Pate']
|
||||
keto_dishes = ['Cauliflower Pizza with Roasted Tomatoes and Chicken', 'Butter Chicken with Grilled Mushrooms, Brussel Sprouts and Mango Chutney', 'Braised Pork Belly with Apple Salad', 'Seared Salmon with Pickled Vegetable and Watercress Salad', 'Pork Chops with Grilled Castelfranco Radicchio and Asparagus', 'Flank Steak with Chimichurri and Asparagus']
|
||||
keto_appetizer_names = ['Kingfish Lettuce Cups','Prawn and Herb Omelette','Satay Steak Skewers','Parmesan Crackers and Spinach Crackers with Salmon Pate']
|
||||
|
||||
|
||||
omnivore = ['Beachside Snapper', 'Governor Shrimp Tacos', 'Shrimp, Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', 'Burnt Masala Wings with Classic Coleslaw', 'Dahi Puri with Black Chickpeas', 'Brisket with Grilled Rhubarb, Onions, and Fennel', 'Roast Leg of Lamb with Crispy Moroccan Carrots & Couscous', 'Baked Chicken Jollof Rice', 'Seafood Risotto', 'Lamb over Marinated Summer Squash with Hazelnuts and Ricotta', 'Beachside Snapper', 'Burnt Masala Wings with Classic Coleslaw', 'Roast Leg of Lamb with Crispy Moroccan Carrots & Couscous', 'Lamb over Marinated Summer Squash with Hazelnuts and Ricotta']
|
||||
omnivore_appetizers = ['Governor Shrimp Tacos', 'Burnt Masala Wings with Classic Coleslaw', 'Dahi Puri with Black Chickpeas', 'Seafood Risotto', 'Governor Shrimp Tacos', 'Seafood Risotto']
|
||||
omnivore_dishes = ['Lamb over Marinated Summer Squash with Hazelnuts and Ricotta', 'Shrimp, Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', 'Brisket with Grilled Rhubarb, Onions, and Fennel', 'Roast Leg of Lamb with Crispy Moroccan Carrots & Couscous', 'Beachside Snapper', 'Baked Chicken Jollof Rice']
|
||||
omnivore_appetizer_names = ['Governor Shrimp Tacos','Burnt Masala Wings with Classic Coleslaw','Dahi Puri with Black Chickpeas','Seafood Risotto']
|
||||
|
||||
|
||||
dishes_and_appetizers = ((vegan, vegan_appetizers),
|
||||
(vegetarian, vegetarian_appetizers),
|
||||
(paleo, paleo_appetizers),
|
||||
(keto, keto_appetizers),
|
||||
(omnivore, omnivore_appetizers)
|
||||
)
|
||||
|
||||
dishes_cleaned = (vegan_dishes,
|
||||
vegetarian_dishes,
|
||||
paleo_dishes,
|
||||
keto_dishes,
|
||||
omnivore_dishes
|
||||
)
|
||||
|
||||
|
||||
#######################################
|
||||
# Data for test_singleton_ingredients #
|
||||
#######################################
|
||||
|
||||
|
||||
intersections = (VEGAN_INTERSECTIONS, VEGETARIAN_INTERSECTIONS, PALEO_INTERSECTIONS,
|
||||
KETO_INTERSECTIONS, OMNIVORE_INTERSECTIONS)
|
||||
|
||||
dishes_and_overlap = [(item[0], item[1]) for item in zip(ingredients_only, intersections)]
|
||||
ingredients = (set.union(*group) for group in ingredients_only)
|
||||
singletons = (item[0] ^ item[1] for item in zip(ingredients, intersections))
|
||||
|
||||
backup_singletons = [{'black-eyed peas', 'coriander', 'cashews', 'yellow split peas', 'pomegranate seeds', 'cumin', 'mangoes', 'pomegranate concentrate', 'red chili powder', 'slivered almonds', 'black peppercorn', 'cornstarch', 'smoked tofu', 'curry leaves', 'zucchini', 'currants', 'dried cranberries', 'yukon gold potato', 'tofu', 'yeast', 'fresh basil', 'hot water', 'ripe plantains', 'calabash nutmeg', 'green beans', 'kosher salt', 'grains of selim', 'vegetarian worcestershire sauce', 'cumin seeds', 'figs', 'ground turmeric', 'white rice', 'harissa', 'garlic powder', 'scallions', 'barberries', 'walnuts', 'basmati rice', 'saffron powder', 'butternut squash', 'thyme', 'tomato', 'chopped parsley', 'hing', 'coriander seeds', 'turmeric powder', 'eggplants', 'sesame oil', "za'atar", 'pareve puff pastry', 'firm tofu', 'yellow onions', 'coriander powder', 'parsley', 'garlic paste', 'rice vinegar', 'sorghum stems', 'spring onions', 'raisins', 'chinese eggplants', 'garam masala', 'ground almonds', 'baking soda', 'clove powder', 'allspice powder', 'parev shortcrust pastry', 'dill', 'nigella seeds', 'dried blueberries', 'cardamom powder', 'cilantro', 'serrano chili', 'breadcrumbs', 'mango powder', 'dried cherries', 'oregano', 'fresh red chili', 'pecans', 'chives', 'spaghetti', 'mixed herbs', 'brandy', 'cumin powder', 'silken tofu', 'yellow onion', 'balsamic vinegar', 'persian cucumber', 'red bell pepper', 'peanuts', 'siracha', 'red pepper flakes', 'spring onion', 'vegan unsweetened yoghurt', 'corn', 'khmeli suneli', 'barley malt', 'green onions', 'apples', 'corn flour', 'honey', 'celeriac', 'bulgur', 'sesame seeds', 'mashed potatoes', 'chili flakes', 'vegetable oil'},
|
||||
{'vegetarian fish sauce', 'cashews', 'white wine', 'portobello mushrooms', 'marmite', 'dates', 'tomatillos', 'cumin', 'chestnuts', 'beets', 'masa', 'mint', 'smoked tofu', 'fresh pea tendrils', 'puff pastry sheets', 'zucchini', 'currants', 'hazelnuts', 'croutons', 'pearl barley', 'dijon mustard', 'yukon gold potato', 'fresh tomatoes', 'vermicelli noodles', 'fresh cherry tomatoes', 'celery', 'hot water', 'green beans', 'grated nutmeg', 'roasted corn', 'palm sugar', 'ancho chili', 'fresh corn', 'spring roll wrappers', 'cotija cheese', 'parmesan rind', 'pasta sheets', 'brazil nuts', 'cauliflower', 'butternut squash', 'mint leaves', 'fresh cherry bocconcini', 'crema', 'blue cheese', 'chickpeas', 'pasilla chili', 'black beans', 'wombok', 'capers', 'pine nuts', 'egg', 'shiitake mushrooms', 'red thai chili', 'jalapeño chili', 'toasted peanuts', 'brussel sprouts', 'lime juice', 'leeks', 'flour', 'dried lasagna noodles', 'onions', 'limes', 'chipotle chili', 'lacinato kale', 'fresh pumpkin', 'almonds', 'olives', 'onion', 'fresh artichoke hearts', 'leek', 'pecans', 'chives', 'blanched almonds', 'nutmeg', 'fresh or frozen fava beans', 'soy sauce', 'avocados', 'bean sprouts', 'asparagus', 'feta cheese', 'sharp white cheddar', 'apples', 'sunflower oil', 'corn flour', 'avocado', 'puff pastry', 'red cabbage', 'pomegranate', 'fresh cilantro leaves', 'oil marinated artichokes', 'vegetable oil'},
|
||||
{'treviso', 'cashews', 'mexican oregano', 'pumpkin puree', 'purple sweet potato', 'homemade apricot honey preserves', 'apple cider vinegar', 'homemade tamarind concentrate', 'paleo parmesan cheese', 'pineapple', 'green bell pepper', 'chipotles', 'nutmeg', 'ground cumin', 'coconut yogurt', 'kale', 'mangoes', 'red bell pepper', 'dried apricots', 'garlic powder', 'pepitas', 'white vinegar', 'scallions', 'avocados', 'shredded red cabbage', 'smoked paprika', 'lime juice', 'flank steak', 'fresh parsley', 'shallots', 'chiles de árbol', 'yellow bell pepper', 'white chicken', 'whole chicken', 'chili powder', 'bacon', 'avocado mayonnaise', 'cilantro', 'limes', 'lemons', 'green onions', 'avocado oil', 'cloves', 'lacinato kale', 'lime zest', 'paleo mayonnaise', 'radishes', 'mango', 'dijon mustard', 'mustard seed', 'cider vinegar', 'pork chops', 'castelfranco radicchio', 'water', 'allspice', 'seranno chili', 'cilantro leaves', 'onion', 'tilapia', 'fresh cilantro leaves', 'worcestershire sauce', 'almond butter', 'fresh thai chili', 'fresh red chili', 'chives', 'roma tomatoes'},
|
||||
{'dried fenugreek leaves', 'apple cider vinegar', 'cinnamon sticks', 'roasted chicken', 'cumin', 'mangoes', 'heavy cream', 'micro cilantro', 'white vinegar', 'red chili powder', 'cinnamon powder', 'mustard seeds', 'red wine vinegar', 'mirin', 'cinnamon', 'green chili', 'avocado oil', 'curry leaves', 'star anise', 'dijon mustard', 'crunchy peanut butter', 'grilled king fish', 'chicken', 'fresh basil', 'cashew nuts', 'pink peppercorns', 'pork belly', 'spinach', 'watercress', 'whole small crimini mushrooms', 'coconut flour', 'fresh ginger', 'fennel bulb', 'harissa', 'tahini', 'mozzarella cheese', 'scallions', 'sriacha', 'fresh parsley', 'thyme', 'light soy sauce', 'cream cheese', 'hing', 'coriander seeds', 'sour cream', 'turmeric powder', 'castelfranco radicchio', 'parmesan', 'toasted buckwheat', 'coriander powder', 'dark soy sauce', 'granny smith apples', 'parsley', 'shrimp', 'garlic paste', 'roasted peanuts', 'turmeric', 'carrot', 'garam masala', 'clove powder', 'cucumbers', 'tomato paste', 'almond meal', 'dutch carrot', 'brussel sprouts', 'red and green thai chili', 'shallots', 'nigella seeds', 'cardamom powder', 'watermelon radishes', 'flaxmeal', 'cilantro', 'fennel seeds', 'chipotle chili', 'ghee', 'parmesan cheese', 'radishes', 'pork chops', 'cilantro leaves', 'fresh greek yogurt', 'cardamom', 'mango powder', 'onion', 'oregano', 'fresh red chili', 'pecans', 'salmon fillets', 'basil', 'green cabbage', 'cumin powder', 'almond flour', 'lemon', 'boned chicken', 'oyster sauce', 'soy sauce', 'little gem lettuce heads', 'peanut oil', 'peanuts', 'caster sugar', 'salmon steaks', 'ginger garlic paste', 'green onions', 'vinegar', 'cloves', 'kecap manis', 'avocado', 'chili flakes', 'red chili flakes', 'tomatoes'},
|
||||
{'coriander', 'white wine', 'fish stock', 'apple cider vinegar', 'salsa', 'rhubarb', 'beef brisket', 'cinnamon sticks', 'cumin', 'roasted chicken', 'chicken wings', 'white vinegar', 'sriracha', 'slivered almonds', 'fresh thyme', 'scotch bonnet pepper', 'zucchini', 'hazelnuts', 'pani puri', 'yukon gold potato', 'toasted bread', 'chicken', 'yoghurt', 'maggi cubes', 'couscous', 'roma tomatoes', 'celery seeds', 'chaat masala', 'white pepper', 'black cardamom', 'harissa', 'red snapper', 'green cardamom', 'crushed red pepper flakes', 'tahini', 'mexican crema', 'chiles de árbol', 'tomato', 'baby squid', 'mussels', 'chipotle adobo sauce', 'shelled large shrimp', 'tomato puree', 'chickpeas', 'fresh tortillas', 'flat-leaf parsley', 'anaheim chili', 'parsley', 'shrimp', 'chile manzano', 'vegetable bullion', 'prawns', 'cherry tomatoes', 'marjoram', 'beer', 'green bell pepper', 'date syrup', 'guajillo chile', 'baby scallops', 'yellow mustard', 'black chickpeas', 'bell pepper', 'filo pastry', 'thin sev', 'bacon', 'white wine vinegar', 'limes', 'rice', 'serrano chili', 'brown sugar', 'parmesan cheese', 'poblano chili', 'fennel bulbs', 'clams', 'baby carrot', 'arborio risotto rice', 'oregano', 'oaxaca cheese', 'green cabbage', 'yellow onion', 'balsamic vinegar', 'whole-milk yogurt', 'sugar', 'red bell pepper', 'pepitas', 'red pepper flakes', 'oranges', 'yellow bell pepper', 'summer squash', 'cloves', 'red cabbage', 'black peppercorns', 'fresh ricotta', 'crab legs', 'scallion chutney', 'sesame seeds', 'vegetable oil'}]
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"authors": [
|
||||
"mohanrajanr",
|
||||
"BethanyG"
|
||||
],
|
||||
"contributors": [
|
||||
"BethanyG",
|
||||
"valentin-p",
|
||||
"pranasziaukas"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"list_methods.py"
|
||||
],
|
||||
"test": [
|
||||
"list_methods_test.py"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.py"
|
||||
]
|
||||
},
|
||||
"icon": "spiral-matrix",
|
||||
"blurb": "Learn useful list methods helping Chaitana manage the lines for her colossal roller coaster."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"python","exercise":"chaitanas-colossal-coaster","id":"74ca4126d16e47bc872cd9fcc30f6d12","url":"https://exercism.org/tracks/python/exercises/chaitanas-colossal-coaster","handle":"sanctushck","is_requester":true,"auto_approve":false}
|
||||
@@ -0,0 +1,130 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
We use [pytest][pytest: Getting Started Guide] as our website test runner.
|
||||
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
|
||||
You should also install the following `pytest` plugins:
|
||||
|
||||
- [pytest-cache][pytest-cache]
|
||||
- [pytest-subtests][pytest-subtests]
|
||||
|
||||
Extended information can be found in our website [Python testing guide][Python track tests page].
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
|
||||
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ cd {path/to/exercise-folder-location}
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
|
||||
|
||||
Linux/MacOS
|
||||
```bash
|
||||
$ python3 -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
Windows
|
||||
```powershell
|
||||
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
|
||||
==================== 7 passed in 0.08s ====================
|
||||
```
|
||||
|
||||
|
||||
### Common options
|
||||
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
|
||||
- `-v` : enable verbose output.
|
||||
- `-x` : stop running tests on first failure.
|
||||
- `--ff` : run failures from previous test before running other test cases.
|
||||
|
||||
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
|
||||
|
||||
|
||||
### Fixing warnings
|
||||
|
||||
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
|
||||
|
||||
```bash
|
||||
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
|
||||
```
|
||||
|
||||
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
|
||||
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
|
||||
|
||||
You can also create your own `pytest.ini` file with the following content:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
markers =
|
||||
task: A concept exercise task.
|
||||
```
|
||||
|
||||
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
|
||||
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
|
||||
|
||||
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
|
||||
|
||||
|
||||
### Extending your IDE or Code Editor
|
||||
|
||||
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
|
||||
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
|
||||
|
||||
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
|
||||
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
|
||||
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
|
||||
[pytest-cache]:http://pythonhosted.org/pytest-cache/
|
||||
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
|
||||
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
|
||||
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
|
||||
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
|
||||
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit list_methods.py` 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 [Python track's documentation](https://exercism.org/docs/tracks/python)
|
||||
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
Below are some resources for getting help if you run into trouble:
|
||||
|
||||
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
|
||||
- [The Exercism Community on Discord](https://exercism.org/r/discord)
|
||||
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
|
||||
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
|
||||
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
|
||||
- [Python Community Forums](https://discuss.python.org/)
|
||||
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
|
||||
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
|
||||
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
|
||||
|
||||
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
|
||||
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Hints
|
||||
|
||||
- Make sure you have a good understanding of how to create and update lists.
|
||||
- The Python [documentation on `lists`][python lists] can be really helpful.
|
||||
- The Python [tutorial section on `lists`][more on lists] is also a good resource.
|
||||
|
||||
## 1. Add Me to the queue
|
||||
|
||||
- An `if-else` statement can help you find which ticket type you are dealing with.
|
||||
- You can then `append()` the person to the queue based on the ticket type.
|
||||
|
||||
## 2. Where are my friends
|
||||
|
||||
- You need to find the `index()` of the friend name from the queue.
|
||||
|
||||
## 3. Can I please join them?
|
||||
|
||||
- Since you know the `index()`, you can `insert()` the friend into the queue at that point.
|
||||
|
||||
## 4. Mean person in the queue
|
||||
|
||||
- You know the mean persons name, so you can `remove()` them from the queue.
|
||||
|
||||
## 5. Namefellows
|
||||
|
||||
- `count()`-ing the occurrences of the `name` in the queue could be a good strategy here.
|
||||
|
||||
## 6. Remove the last person
|
||||
|
||||
- Although you could `remove()` the person by name, `pop()`-ing them out might be quicker.
|
||||
|
||||
## 7. Sort the Queue List
|
||||
|
||||
- Don't forget that You need to avoid mutating the queue and losing its original order.
|
||||
- Once you have a `copy()`, `sort()`-ing should be straightforward.
|
||||
- We're looking for an _ascending_ sort, or _alphabetical from a-z_.
|
||||
|
||||
[python lists]: https://docs.python.org/3.11/library/stdtypes.html#list
|
||||
[more on lists]: https://docs.python.org/3.11/tutorial/datastructures.html#more-on-lists
|
||||
@@ -0,0 +1,419 @@
|
||||
# Chaitana's Colossal Coaster
|
||||
|
||||
Welcome to Chaitana's Colossal Coaster on Exercism's Python 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 [`list`][list] is a mutable collection of items in _sequence_.
|
||||
Like most collections (_see the built-ins [`tuple`][tuple], [`dict`][dict] and [`set`][set]_), lists can hold reference to any (or multiple) data type(s) - including other lists.
|
||||
Lists can be copied in whole or in part via [slice notation][slice notation] or through the use of `<list>.copy()`.
|
||||
Like any [sequence][sequence type], elements within `lists` are referenced by `0-based index` number from the left, or `-1-based index` number from the right.
|
||||
|
||||
Lists support both [common][common sequence operations] and [mutable][mutable sequence operations] sequence operations such as `min(<list>)`/`max(<list>)`, `<list>.index()`, `<list>.append()` and `<list>.reverse()`.
|
||||
Elements inside a `list` can be iterated over using the `for item in <list>` construct.
|
||||
`for index, item in enumerate(<list>)` can be used when both the element index and element value are needed.
|
||||
|
||||
Python also provides many useful [list-methods][list-methods] for working with lists.
|
||||
A selection of these `list methods` is covered below.
|
||||
|
||||
|
||||
Note that when you manipulate a `list` with a `list-method`, **you alter the list** object that has been passed.
|
||||
If you do not wish to mutate the original `list`, you will need to at least make a `shallow copy` of it via slice or `<list>.copy()`.
|
||||
|
||||
|
||||
## Adding Items
|
||||
|
||||
To add an item to the end or "right-hand side" of an existing list, use `<list>.append(<item>)`:
|
||||
|
||||
```python
|
||||
>>> numbers = [1, 2, 3]
|
||||
>>> numbers.append(9)
|
||||
|
||||
>>> numbers
|
||||
[1, 2, 3, 9]
|
||||
```
|
||||
|
||||
Rather than _appending_, `<list>.insert()` gives you the ability to add the item to a _specific index_ in the list.
|
||||
It takes 2 parameters:
|
||||
|
||||
1. the `<index>` at which you want the item to be inserted.
|
||||
2. the `<item>` to be inserted.
|
||||
|
||||
**Note**: If the given `index` is 0, the item will be added to the start ("left-hand side") of the `list`.
|
||||
If the supplied `index` is greater than the final `index` on the `list`, the item will be added in the final position -- the equivalent of using `<list>.append(<item>)`.
|
||||
|
||||
|
||||
```python
|
||||
>>> numbers = [1, 2, 3]
|
||||
>>> numbers.insert(0, -2)
|
||||
|
||||
>>> numbers
|
||||
[-2, 1, 2, 3]
|
||||
|
||||
>>> numbers.insert(1, 0)
|
||||
|
||||
>>> numbers
|
||||
[-2, 0, 1, 2, 3]
|
||||
```
|
||||
|
||||
|
||||
`<list>.extend(<item>)` can be used to combine an existing list with the elements from another iterable (for example, a `set`, `tuple`, `str`, or `list`).
|
||||
The iterable is _unpacked_ and elements are appended in order (_Using `<list>.append(<item>)` in this circumstance would add the entire iterable as a **single item**._).
|
||||
|
||||
|
||||
```python
|
||||
>>> numbers = [1, 2, 3]
|
||||
>>> other_numbers = [5, 6, 7]
|
||||
|
||||
>>> numbers.extend(other_numbers)
|
||||
|
||||
>>> numbers
|
||||
[1, 2, 3, 5, 6, 7]
|
||||
|
||||
>>> numbers.extend([8, 9])
|
||||
|
||||
>>> numbers
|
||||
[1, 2, 3, 5, 6, 7, 8, 9]
|
||||
|
||||
>>> numbers.append([8,9])
|
||||
|
||||
>>> numbers
|
||||
[1, 2, 3, 5, 6, 7, 8, 9, [8, 9]]
|
||||
```
|
||||
|
||||
|
||||
## Removing Items
|
||||
|
||||
To delete an item from a list use `<list>.remove(<item>)`, passing the item to be removed as an argument.
|
||||
`<list>.remove(<item>)` will throw a `ValueError` if the item is not present in the `list`.
|
||||
|
||||
|
||||
```python
|
||||
>>> numbers = [1, 2, 3]
|
||||
>>> numbers.remove(2)
|
||||
|
||||
>>> numbers
|
||||
[1, 3]
|
||||
|
||||
# Trying to remove a value that is not in the list throws a ValueError
|
||||
>>> numbers.remove(0)
|
||||
ValueError: list.remove(x): x not in list
|
||||
```
|
||||
|
||||
|
||||
Alternatively, using the `<list>.pop(<index>)` method will both remove **and** `return` an element for use.
|
||||
|
||||
|
||||
`<list>.pop(<index>)` takes one optional parameter: the `index` of the item to be removed and returned.
|
||||
If the (optional) `index` argument is not specified, the final element of the `list` will be removed and returned.
|
||||
If the `index` specified is higher than the final item `index`, an `IndexError` is raised.
|
||||
|
||||
|
||||
```python
|
||||
>>> numbers = [1, 2, 3]
|
||||
|
||||
>>> numbers.pop(0)
|
||||
1
|
||||
|
||||
>>> numbers
|
||||
[2, 3]
|
||||
|
||||
>>> numbers.pop()
|
||||
3
|
||||
|
||||
>>> numbers
|
||||
[2]
|
||||
|
||||
>>> numbers.pop(1)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
IndexError: pop index out of range
|
||||
```
|
||||
|
||||
All elements can be removed from a `list` with `list.clear()`. It doesn't take any parameters.
|
||||
|
||||
```python
|
||||
>>> numbers = [1, 2, 3]
|
||||
>>> numbers.clear()
|
||||
|
||||
>>> numbers
|
||||
[]
|
||||
```
|
||||
|
||||
## Reversing and reordering
|
||||
|
||||
The `<list>.reverse()` method will reverse the order of elements **in-place**.
|
||||
|
||||
|
||||
```python
|
||||
>>> numbers = [1, 2, 3]
|
||||
>>> numbers.reverse()
|
||||
|
||||
>>> numbers
|
||||
[3, 2, 1]
|
||||
```
|
||||
|
||||
|
||||
A list can be re-ordered _**in place**_ with the help of [`<list>.sort()`][sort].
|
||||
Default sort order is _ascending_ from the left.
|
||||
The Python docs offer [additional tips and techniques for sorting][sorting how to].
|
||||
|
||||
~~~~exercism/note
|
||||
From 2002 to 2022, Python used an algorithm called [`Timsort`][timsort] internally to arrange lists, but switched to [`Powersort`][powersort] from `Python 3.11` onward.
|
||||
|
||||
[powersort]: https://www.wild-inter.net/publications/munro-wild-2018
|
||||
[timsort]: https://en.wikipedia.org/wiki/Timsort
|
||||
~~~~
|
||||
|
||||
|
||||
```python
|
||||
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
|
||||
|
||||
# The default sort order is *ascending*.
|
||||
>>> names.sort()
|
||||
|
||||
>>> names
|
||||
["Bruce", "Natasha", "Thor", "Tony"]
|
||||
```
|
||||
|
||||
If a _descending_ order is desired, pass the `reverse=True` argument:
|
||||
|
||||
```python
|
||||
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
|
||||
>>> names.sort(reverse=True)
|
||||
|
||||
>>> names
|
||||
["Tony", "Thor", "Natasha", "Bruce"]
|
||||
```
|
||||
|
||||
For cases where mutating the original list is undesirable, the built-in [`sorted(<iterable>)`][sorted] function can be used to return a sorted **copy**.
|
||||
|
||||
|
||||
```python
|
||||
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
|
||||
|
||||
>>> sorted(names)
|
||||
['Bruce', 'Natasha', 'Thor', 'Tony']
|
||||
```
|
||||
|
||||
|
||||
## Occurrences of an item in a list
|
||||
|
||||
The number of occurrences of an element in a list can be calculated with the help of `list.count(<item>)`.
|
||||
It takes the `item` to be counted as its argument and returns the total number of times that element appears in the `list`.
|
||||
|
||||
|
||||
```python
|
||||
>>> items = [1, 4, 7, 8, 2, 9, 2, 1, 1, 0, 4, 3]
|
||||
|
||||
>>> items.count(1)
|
||||
3
|
||||
```
|
||||
|
||||
## Finding the index of items
|
||||
|
||||
`<list>.index(<item>)` will return the `index` number of the _first occurrence_ of an item passed in.
|
||||
If there are no occurrences, a `ValueError` is raised.
|
||||
If the exact position of an item isn't needed, the built-in `in` operator is more efficient for checking if a list contains a given value.
|
||||
|
||||
|
||||
Indexing is zero-based from the left, so the position of the "first" item is `0`.
|
||||
Indexing will also work from the right, beginning with `-1`.
|
||||
|
||||
|
||||
```python
|
||||
>>> items = [7, 4, 1, 0, 2, 5]
|
||||
|
||||
>>> items.index(4)
|
||||
1
|
||||
|
||||
>>> items.index(10)
|
||||
ValueError: 10 is not in list
|
||||
```
|
||||
|
||||
`start` and `end` indices can also be provided to narrow the search to a specific section of the `list`:
|
||||
|
||||
```python
|
||||
>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"]
|
||||
|
||||
>>> names.index("Tina")
|
||||
0
|
||||
|
||||
>>> names.index("Tina", 2, 5)
|
||||
3
|
||||
```
|
||||
|
||||
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
|
||||
[dict]: https://docs.python.org/3/library/stdtypes.html#dict
|
||||
[list-methods]: https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
|
||||
[list]: https://docs.python.org/3/library/stdtypes.html#list
|
||||
[mutable sequence operations]: https://docs.python.org/3/library/stdtypes.html#typesseq-mutable
|
||||
[sequence type]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
|
||||
[set]: https://docs.python.org/3/library/stdtypes.html#set
|
||||
[slice notation]: https://docs.python.org/3/reference/expressions.html#slicings
|
||||
[sort]: https://docs.python.org/3/library/stdtypes.html#list.sort
|
||||
[sorted]: https://docs.python.org/3/library/functions.html#sorted
|
||||
[sorting how to]: https://docs.python.org/3/howto/sorting.html
|
||||
[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
|
||||
|
||||
## Instructions
|
||||
|
||||
Chaitana owns a very popular theme park.
|
||||
She only has one ride in the very center of beautifully landscaped grounds: The Biggest Roller Coaster in the World(TM).
|
||||
Although there is only this one attraction, people travel from all over the world and stand in line for hours for the opportunity to ride Chaitana's hypercoaster.
|
||||
|
||||
There are two queues for this ride, each represented as a `list`:
|
||||
|
||||
1. Normal Queue
|
||||
2. Express Queue (_also known as the Fast-track_) - where people pay extra for priority access.
|
||||
|
||||
|
||||
You have been asked to write some code to better manage the guests at the park.
|
||||
You need to implement the following functions as soon as possible before the guests (and your boss, Chaitana!) get cranky.
|
||||
Make sure you read carefully.
|
||||
Some tasks ask that you change or update the existing queue, while others ask you to make a copy of it.
|
||||
|
||||
|
||||
## 1. Add me to the queue
|
||||
|
||||
Define the `add_me_to_the_queue()` function that takes 4 parameters `<express_queue>, <normal_queue>, <ticket_type>, <person_name>` and returns the appropriate queue updated with the person's name.
|
||||
|
||||
|
||||
1. `<ticket_type>` is an `int` with 1 == express_queue and 0 == normal_queue.
|
||||
2. `<person_name>` is the name (as a `str`) of the person to be added to the respective queue.
|
||||
|
||||
|
||||
```python
|
||||
>>> add_me_to_the_queue(express_queue=["Tony", "Bruce"], normal_queue=["RobotGuy", "WW"], ticket_type=1, person_name="RichieRich")
|
||||
...
|
||||
["Tony", "Bruce", "RichieRich"]
|
||||
|
||||
>>> add_me_to_the_queue(express_queue=["Tony", "Bruce"], normal_queue=["RobotGuy", "WW"], ticket_type=0, person_name="HawkEye")
|
||||
....
|
||||
["RobotGuy", "WW", "HawkEye"]
|
||||
```
|
||||
|
||||
## 2. Where are my friends?
|
||||
|
||||
One person arrived late at the park but wants to join the queue where their friends are waiting.
|
||||
But they have no idea where their friends are standing and there isn't any phone reception to call them.
|
||||
|
||||
Define the `find_my_friend()` function that takes 2 parameters `queue` and `friend_name` and returns the position in the queue of the person's name.
|
||||
|
||||
|
||||
1. `<queue>` is the `list` of people standing in the queue.
|
||||
2. `<friend_name>` is the name of the friend whose index (place in the queue) you need to find.
|
||||
|
||||
Remember: Indexing starts at 0 from the left, and -1 from the right.
|
||||
|
||||
|
||||
```python
|
||||
>>> find_my_friend(queue=["Natasha", "Steve", "T'challa", "Wanda", "Rocket"], friend_name="Steve")
|
||||
...
|
||||
1
|
||||
```
|
||||
|
||||
|
||||
## 3. Can I please join them?
|
||||
|
||||
Now that their friends have been found (in task #2 above), the late arriver would like to join them at their place in the queue.
|
||||
Define the `add_me_with_my_friends()` function that takes 3 parameters `queue`, `index`, and `person_name`.
|
||||
|
||||
|
||||
1. `<queue>` is the `list` of people standing in the queue.
|
||||
2. `<index>` is the position at which the new person should be added.
|
||||
3. `<person_name>` is the name of the person to add at the index position.
|
||||
|
||||
Return the queue updated with the late arrivals name.
|
||||
|
||||
|
||||
```python
|
||||
>>> add_me_with_my_friends(queue=["Natasha", "Steve", "T'challa", "Wanda", "Rocket"], index=1, person_name="Bucky")
|
||||
...
|
||||
["Natasha", "Bucky", "Steve", "T'challa", "Wanda", "Rocket"]
|
||||
```
|
||||
|
||||
## 4. Mean person in the queue
|
||||
|
||||
You just heard from the queue that there is a really mean person shoving, shouting, and making trouble.
|
||||
You need to throw that miscreant out for bad behavior!
|
||||
|
||||
|
||||
Define the `remove_the_mean_person()` function that takes 2 parameters `queue` and `person_name`.
|
||||
|
||||
|
||||
1. `<queue>` is the `list` of people standing in the queue.
|
||||
2. `<person_name>` is the name of the person that needs to be kicked out.
|
||||
|
||||
Return the queue updated without the mean person's name.
|
||||
|
||||
```python
|
||||
>>> remove_the_mean_person(queue=["Natasha", "Steve", "Eltran", "Wanda", "Rocket"], person_name="Eltran")
|
||||
...
|
||||
["Natasha", "Steve", "Wanda", "Rocket"]
|
||||
```
|
||||
|
||||
|
||||
## 5. Namefellows
|
||||
|
||||
You may not have seen two unrelated people who look exactly the same, but you have _definitely_ seen unrelated people with the exact same name (_namefellows_)!
|
||||
Today, it looks like there are a lot of them in attendance.
|
||||
You want to know how many times a particular name occurs in the queue.
|
||||
|
||||
Define the `how_many_namefellows()` function that takes 2 parameters `queue` and `person_name`.
|
||||
|
||||
1. `<queue>` is the `list` of people standing in the queue.
|
||||
2. `<person_name>` is the name you think might occur more than once in the queue.
|
||||
|
||||
|
||||
Return the number of occurrences of `person_name`, as an `int`.
|
||||
|
||||
|
||||
```python
|
||||
>>> how_many_namefellows(queue=["Natasha", "Steve", "Eltran", "Natasha", "Rocket"], person_name="Natasha")
|
||||
...
|
||||
2
|
||||
```
|
||||
|
||||
## 6. Remove the last person
|
||||
|
||||
Sadly, it's overcrowded at the park today and you need to remove the last person in the normal line (_you will give them a voucher to come back in the fast-track on another day_).
|
||||
You will have to define the function `remove_the_last_person()` that takes 1 parameter `queue`, which is the list of people standing in the queue.
|
||||
|
||||
You should update the `list` and also `return` the name of the person who was removed, so you can write them a voucher.
|
||||
|
||||
|
||||
```python
|
||||
>>> remove_the_last_person(queue=["Natasha", "Steve", "Eltran", "Natasha", "Rocket"])
|
||||
...
|
||||
'Rocket'
|
||||
```
|
||||
|
||||
## 7. Sort the Queue List
|
||||
|
||||
For administrative purposes, you need to get all the names in a given queue in alphabetical order.
|
||||
|
||||
|
||||
Define the `sorted_names()` function that takes 1 argument, `queue`, (the `list` of people standing in the queue), and returns a `sorted` copy of the `list`.
|
||||
|
||||
|
||||
```python
|
||||
>>> sorted_names(queue=["Natasha", "Steve", "Eltran", "Natasha", "Rocket"])
|
||||
...
|
||||
['Eltran', 'Natasha', 'Natasha', 'Rocket', 'Steve']
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @mohanrajanr
|
||||
- @BethanyG
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @BethanyG
|
||||
- @valentin-p
|
||||
- @pranasziaukas
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user