From d39d448aa43a2008390635e0a87c0986b20c6525 Mon Sep 17 00:00:00 2001 From: Carl Date: Wed, 21 Dec 2022 20:49:38 +0100 Subject: [PATCH 01/15] Start --- concepts/itertools/about.md | 337 +++++++++++++++++++++++++++++++++- concepts/itertools/links.json | 4 +- 2 files changed, 338 insertions(+), 3 deletions(-) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index c628150d56..82e6c6161a 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -1,2 +1,337 @@ -#TODO: Add about for this concept. +## Infinite iterators +```exercism/note +To avoid infinite loops, you can use `break` to end a loop. +``` + +### Count() + +`count(start, )` allows to print all values from the start value to infinity. +Count also has an optional step parameter, which allows to print values with a step size other than 1. + +```python +>>> import itertools +>>> for number in itertools.count(5, 2): +... if number > 20: +... break +... else: +... print(number, end=' ') +... +5 7 9 11 13 15 17 19 +``` + +Giving `count()` a negative step size will print values in a descending order. + +```python +>>> import itertools +>>> for number in itertools.count(5, -2): +... if number < -20: +... break +... else: +... print(number, end=' ') +... +5 3 1 -1 -3 -5 -7 -9 -11 -13 -15 -17 -19 +``` + +### Cycle() + +`cycle(iterable)` allows to print all values from the iterable in an infinte loop. +For example a `list`, `tuple`, `string` or `dict` can be used as an iterable. + +```python +>>> import itertools +>>> number = 0 +>>> for letter in itertools.cycle("ABC"): +... if number == 10: +... break +... else: +... print(letter, end=' ') +... number += 1 +... +A B C A B C A B C A +``` + +### Repeat() + +`repeat(object, )` allows to print the same value in an infinte loop. +Although if the optional times parameter is given, the value will be printed that many times. +Meaning that it is not an infinite loop if that parameter is given. + +```python +>>> import itertools +>>> for number in itertools.repeat(5, 3): +... print(number, end=' ') +... +5 5 5 +``` + +## Iterators terminating on the shortest input sequence + +### Accumulate() + +`accumulate(iterable, )` allows to print the accumulated values. + +perhaps skip + +### Chain() + +`chain(iterable1, iterable2...)` creates an irretator of values from the iterables in the order they are given. + +```python +>>> import itertools +>>> for number in itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]): +... print(number, end=' ') +... +1 2 3 4 5 6 7 8 9 +``` + +Chain can also be used to concate a different amount of iterables to a list or tuple by using the `list()` or `tuple()` function. + +```python +>>> import itertools +>>> list(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9])) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +``` + +### chain.from_iterable() + +Works like chain but takes a single iterable argument and unpack that iterable into individual iterables. + +```python +>>> import itertools +>>> for number in itertools.chain.from_iterable([[1, 2, 3], [4, 5, 6], [7, 8, 9]]): +... print(number, end=' ') +... +1 2 3 4 5 6 7 8 9 +``` + +### Compress() + +`compress(iterable, selectors)` creates an irretator from the iterable where the corresponding selector is `True`. +The selector can hold bools but can also hold integers, where 0 is `False` and any other integer is `True`. + +```python +>>> import itertools +>>> for letter in itertools.compress("Exercism", [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]): +... print(letter, end=' ') +... +E r c s m +``` + +### Dropwhile() + +skip + +### Filterfalse() + +skip + +### Groupby() + +probably skip + +### Islice() + +`islice(iterable, , , )` creates an irretator of the values from the iterable, from the start index to the stop index with a step size of step. + +```python +>>> import itertools +>>> for letter in itertools.islice("Exercism", 2, 5, 2): +... print(letter, end=' ') +... +e c +``` + +### Pairwise() + +```exercism/note +`Pairwise()` requires Python 3.10+. +If you are using the online editor then you don't need to worry about this. +``` + +`Pairwise(iterable)` was intruduced in Python 3.10 and returns an iterator of overlapping pairs of values from the input iterable. + +```python +>>> import itertools +>>> for pair in itertools.pairwise("Exercism"): +... print(pair, end=' ') +... +('E', 'x') ('x', 'e') ('e', 'r') ('r', 'c') ('c', 'i') ('i', 's') ('s', 'm') +``` + +### Starmap() + +Pehaps skip + +### Takewhile() + +skip + +### Tee() + +Talk with Bethany about + +### Zip_longest() + +#### Explaning zip + +```exercism/caution +Zip in computer science(programming) is not the same as zip in computer terms. +Zip in computer terms reffers to a file format that supports compression. +While zip in computer science(programming) reffers to the `zip()` function. +``` + +`zip()` is a built in function and is not apart of the `itertools` module. +It takes any number of iterables and returns an iterator of tuples. +Where the i-th tuple contains the i-th element from each of the argument iterables. +Meaning the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable and so on. + +```python +>>> zipped = zip(['x', 'y', 'z'], [1, 2, 3], [True, False, True]) +>>> list(zipped) +[('x', 1, True),('y', 2, False), ('z', 3, True)] +``` + +If the iterables are not the same length, then the iterator will stop when the shortest iterable is exhausted. + +```python +>>> zipped = zip(['x', 'y', 'z'], [1, 2, 3, 4], [True, False]) +>>> list(zipped) +[('x', 1, True),('y', 2, False)] +``` + +#### Explaning zip_longest + +`zip_longest(iterator, )` is a function from the `itertools` module. +The difference between `zip_longest()` and `zip()` is that `zip_longest()` will continue until the longest iterable is exhausted. +If the iterables are not the same length the `fillvalue` will be used to fill in the missing values. +By the default the `fillvalue` is `None`. + +```python +>>> import itertools +>>> zipped = itertools.zip_longest(['x', 'y', 'z'], [1, 2, 3, 4], [True, False]) +>>> list(zipped) +[('x', 1, True),('y', 2, False), ('z', 3, None), (None, 4, None)] +``` + +An example where a fillvalue is given: + +```python +>>> import itertools +>>> zipped = itertools.zip_longest(['x', 'y', 'z'], [1, 2, 3, 4], [True, False], fillvalue='fill') +>>> list(zipped) +[('x', 1, True),('y', 2, False), ('z', 3, 'fill'), ('fill', 4, 'fill')] +``` + +## Combinatoric iterators + +### Product() + +`product(iterable1, iterable2..., )` creates an iterator of tuples where the i-th tuple contains the i-th element from each of the argument iterables. +The repeat keyword argument can be used to specify the number of times the input iterables are repeated. +By default the repeat keyword argument is 1. + +```python +>>> import itertools +>>> for product in itertools.product("ABCD", repeat=1): +... print(product, end=' ') +... +('A',) ('B',) ('C',) ('D',) +``` + +```python +>>> import itertools +>>> for product in itertools.product("ABCD", repeat=2): +... print(product, end=' ') +... +('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'C') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C') ('D', 'D') +``` + +The last one here can be seen as doing a nested for loop. +When you increase the repeat value the number of iterations increases exponentially. +The example above is a n\*\*2 iteration. + +```python +>>> import itertools +>>> for letter1 in "ABCD": +... for letter2 in "ABCD": +... print((letter1, letter2), end=' ') +... +('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'C') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C') ('D', 'D') +``` + +You can also give it multiple iterables. + +```python +>>> import itertools +>>> for product in itertools.product("ABCD", "xy" repeat=1): +... print(product, end=' ') +... +('A', 'x') ('A', 'y') ('B', 'x') ('B', 'y') ('C', 'x') ('C', 'y') ('D', 'x') ('D', 'y') +``` + +Here is an example of doing it wihout `product()`. +It looks similliar to the last example but since we have two iterables we need to nest the for loops. +Even though the proudct is given repeat=1. +The reasson to why it is only 2 for loops earlier was because we only had one iterable. +If we had two iterables and gave it repeat=2 we would need 4 for loops. +Since 2 \* 2 = 4. + +```python +>>> for letter1 in "ABCD": +... for letter2 in "xy": +... print((letter1, letter2), end=' ') +... +('A', 'x') ('A', 'y') ('B', 'x') ('B', 'y') ('C', 'x') ('C', 'y') ('D', 'x') ('D', 'y') +``` + +### Permutations() + +`permutations(iterable, )` creates an iterator of tuples. +It works like `product()` but it doesnt repeat values from a specific positoon from the iterable and can only take one iterable. +The "r" keyword argument can be used to specify the number of times the input iterables are repeated. +By default the "r" keyword argument is None. +If "r" is None then the length of the iterable is used. + +```python +>>> import itertools +>>> for permutation in itertools.permutations("ABCD", repeat=1): +... print(permutation, end=' ') +... +('A',) ('B',) ('C',) ('D',) +``` + +```python +>>> import itertools +>>> for permutation in itertools.permutations("ABCD", repeat=2): +... print(permutation, end=' ') +... +('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C') +``` + +### Combinations() + +`combinations(iterable, r)` finds all the possible combinations of the given iterable. +The r keyword argument is used to specify the length of the tuples generated. + +```python +>>> import itertools +>>> for combination in itertools.combinations("ABCD", 2): +... print(combination, end=' ') +... +('A', 'B') ('A', 'C') ('A', 'D') ('B', 'C') ('B', 'D') ('C', 'D') +``` + +### Combinations_with_replacement() + +`combinations_with_replacement(iterable, r)` finds all the possible combinations of the given iterable. +The r keyword argument is used to specify the length of the tuples generated. +The difference between this and `combinations()` is that it can repeat values. + +```python +>>> import itertools +>>> for combination in itertools.combinations_with_replacement("ABCD", 2): +... print(combination, end=' ') +... +('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'C') ('C', 'D') ('D', 'D') +``` diff --git a/concepts/itertools/links.json b/concepts/itertools/links.json index eb5fb7c38a..8a44906b3f 100644 --- a/concepts/itertools/links.json +++ b/concepts/itertools/links.json @@ -1,7 +1,7 @@ [ { - "url": "http://example.com/", - "description": "TODO: add new link (above) and write a short description here of the resource." + "url": "https://docs.python.org/3/library/itertools.htm", + "description": "Offical Python documentation for the itertools module." }, { "url": "http://example.com/", From d2630c8666a7f8c1f1af151d0cfa0d49e428a186 Mon Sep 17 00:00:00 2001 From: Carl Date: Thu, 22 Dec 2022 16:05:04 +0100 Subject: [PATCH 02/15] Added the changes we talked about and some extras --- concepts/itertools/.meta/config.json | 2 +- concepts/itertools/about.md | 195 +++++++++++++-------------- 2 files changed, 93 insertions(+), 104 deletions(-) diff --git a/concepts/itertools/.meta/config.json b/concepts/itertools/.meta/config.json index 9b9e8da5a9..3bf2b514cc 100644 --- a/concepts/itertools/.meta/config.json +++ b/concepts/itertools/.meta/config.json @@ -1,5 +1,5 @@ { "blurb": "TODO: add blurb for this concept", - "authors": ["bethanyg", "cmccandless"], + "authors": ["bethanyg", "meatball133"], "contributors": [] } diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index 82e6c6161a..e23860db98 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -1,78 +1,5 @@ -## Infinite iterators - -```exercism/note -To avoid infinite loops, you can use `break` to end a loop. -``` - -### Count() - -`count(start, )` allows to print all values from the start value to infinity. -Count also has an optional step parameter, which allows to print values with a step size other than 1. - -```python ->>> import itertools ->>> for number in itertools.count(5, 2): -... if number > 20: -... break -... else: -... print(number, end=' ') -... -5 7 9 11 13 15 17 19 -``` - -Giving `count()` a negative step size will print values in a descending order. - -```python ->>> import itertools ->>> for number in itertools.count(5, -2): -... if number < -20: -... break -... else: -... print(number, end=' ') -... -5 3 1 -1 -3 -5 -7 -9 -11 -13 -15 -17 -19 -``` - -### Cycle() - -`cycle(iterable)` allows to print all values from the iterable in an infinte loop. -For example a `list`, `tuple`, `string` or `dict` can be used as an iterable. - -```python ->>> import itertools ->>> number = 0 ->>> for letter in itertools.cycle("ABC"): -... if number == 10: -... break -... else: -... print(letter, end=' ') -... number += 1 -... -A B C A B C A B C A -``` - -### Repeat() - -`repeat(object, )` allows to print the same value in an infinte loop. -Although if the optional times parameter is given, the value will be printed that many times. -Meaning that it is not an infinite loop if that parameter is given. - -```python ->>> import itertools ->>> for number in itertools.repeat(5, 3): -... print(number, end=' ') -... -5 5 5 -``` - ## Iterators terminating on the shortest input sequence -### Accumulate() - -`accumulate(iterable, )` allows to print the accumulated values. - -perhaps skip - ### Chain() `chain(iterable1, iterable2...)` creates an irretator of values from the iterables in the order they are given. @@ -95,11 +22,16 @@ Chain can also be used to concate a different amount of iterables to a list or t ### chain.from_iterable() -Works like chain but takes a single iterable argument and unpack that iterable into individual iterables. +Works like chain but takes a single nested iterable, unpacking that iterable into individual iterables. ```python >>> import itertools ->>> for number in itertools.chain.from_iterable([[1, 2, 3], [4, 5, 6], [7, 8, 9]]): +>>> for number in itertools.chain.from_iterable( + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ]): ... print(number, end=' ') ... 1 2 3 4 5 6 7 8 9 @@ -107,32 +39,30 @@ Works like chain but takes a single iterable argument and unpack that iterable i ### Compress() -`compress(iterable, selectors)` creates an irretator from the iterable where the corresponding selector is `True`. -The selector can hold bools but can also hold integers, where 0 is `False` and any other integer is `True`. +`compress(iterable, selectors)` creates an iterator from the input iterable where the corresponding selector is `True`. +The selector can be `True`/`False` or integers, where 0 is `False` and 1 is `True`. ```python >>> import itertools ->>> for letter in itertools.compress("Exercism", [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]): +>>> for letter in itertools.compress("Exercism", [1, 0, 0, 1, 1, 0, 1, 1]): ... print(letter, end=' ') ... E r c s m ``` -### Dropwhile() - -skip - -### Filterfalse() +With `True`/`False`: -skip - -### Groupby() - -probably skip +```python +>>> import itertools +>>> for letter in itertools.compress("Exercism", [True, False, False, True, True, False, True, True]): +... print(letter, end=' ') +... +E r c s m +``` ### Islice() -`islice(iterable, , , )` creates an irretator of the values from the iterable, from the start index to the stop index with a step size of step. +`islice(iterable, start, , )` creates a new iterator from the slice (from the start index to the stop index with a step size of step). ```python >>> import itertools @@ -159,14 +89,6 @@ If you are using the online editor then you don't need to worry about this. ('E', 'x') ('x', 'e') ('e', 'r') ('r', 'c') ('c', 'i') ('i', 's') ('s', 'm') ``` -### Starmap() - -Pehaps skip - -### Takewhile() - -skip - ### Tee() Talk with Bethany about @@ -176,15 +98,13 @@ Talk with Bethany about #### Explaning zip ```exercism/caution -Zip in computer science(programming) is not the same as zip in computer terms. -Zip in computer terms reffers to a file format that supports compression. -While zip in computer science(programming) reffers to the `zip()` function. +Pythons `zip()` function should not be confused with the zip compression format. ``` `zip()` is a built in function and is not apart of the `itertools` module. It takes any number of iterables and returns an iterator of tuples. Where the i-th tuple contains the i-th element from each of the argument iterables. -Meaning the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable and so on. +For example, the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable, and so on until the shortest iterable is exhausted. ```python >>> zipped = zip(['x', 'y', 'z'], [1, 2, 3], [True, False, True]) @@ -203,8 +123,8 @@ If the iterables are not the same length, then the iterator will stop when the s #### Explaning zip_longest `zip_longest(iterator, )` is a function from the `itertools` module. -The difference between `zip_longest()` and `zip()` is that `zip_longest()` will continue until the longest iterable is exhausted. -If the iterables are not the same length the `fillvalue` will be used to fill in the missing values. +Unlink `zip()`, it will not stop when the shortest iterable is exhausted. +If the iterables are not the same length, `fillvalue` will be used to pad missing values. By the default the `fillvalue` is `None`. ```python @@ -239,6 +159,8 @@ By default the repeat keyword argument is 1. ('A',) ('B',) ('C',) ('D',) ``` +Giving a repeat value of 2: + ```python >>> import itertools >>> for product in itertools.product("ABCD", repeat=2): @@ -335,3 +257,70 @@ The difference between this and `combinations()` is that it can repeat values. ... ('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'C') ('C', 'D') ('D', 'D') ``` + +## Infinite iterators + +```exercism/note +To avoid infinite loops, you can use `break` to end a loop. +``` + +### Count() + +`count(start, )` produces all values from the start value to infinity. +Count also has an optional step parameter, which will produce values with a step size other than 1. + +```python +>>> import itertools +>>> for number in itertools.count(5, 2): +... if number > 20: +... break +... else: +... print(number, end=' ') +... +5 7 9 11 13 15 17 19 +``` + +Giving `count()` a negative step size will produces values in a descending order. + +```python +>>> import itertools +>>> for number in itertools.count(5, -2): +... if number < -20: +... break +... else: +... print(number, end=' ') +... +5 3 1 -1 -3 -5 -7 -9 -11 -13 -15 -17 -19 +``` + +### Cycle() + +`cycle(iterable)` produces all values from the iterable in an infinte loop. +A `list`, `tuple`, `string`, `dict` or any other iterable can be used. + +```python +>>> import itertools +>>> number = 0 +>>> for letter in itertools.cycle("ABC"): +... if number == 10: +... break +... else: +... print(letter, end=' ') +... number += 1 +... +A B C A B C A B C A +``` + +### Repeat() + +`repeat(object, )` produces the same value in an infinte loop. +Although if the optional times parameter is given, the value will produces that many times. +Meaning that it is not an infinite loop if that parameter is given. + +```python +>>> import itertools +>>> for number in itertools.repeat(5, 3): +... print(number, end=' ') +... +5 5 5 +``` From af300a7414c305c2d7f050d1eba961a627fbb27a Mon Sep 17 00:00:00 2001 From: Carl Date: Fri, 23 Dec 2022 17:01:41 +0100 Subject: [PATCH 03/15] Added extra parts --- concepts/itertools/about.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index e23860db98..4cfe5bca53 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -14,12 +14,22 @@ Chain can also be used to concate a different amount of iterables to a list or tuple by using the `list()` or `tuple()` function. +Using `list()`: + ```python >>> import itertools >>> list(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9])) [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` +Using `tuple()`: + +```python +>>> import itertools +>>> tuple(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9])) +(1, 2, 3, 4, 5, 6, 7, 8, 9) +``` + ### chain.from_iterable() Works like chain but takes a single nested iterable, unpacking that iterable into individual iterables. From c76f30679166ee816a6ab64db67279fe43d62adb Mon Sep 17 00:00:00 2001 From: Carl Date: Sat, 24 Dec 2022 23:34:24 +0100 Subject: [PATCH 04/15] Added blurb --- concepts/itertools/about.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index 4cfe5bca53..366515cf1a 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -270,6 +270,10 @@ The difference between this and `combinations()` is that it can repeat values. ## Infinite iterators +Most of iterator from the `itertools` module get exhausted after a time. +But there are some that are infinite, these are known as infinte iterators. +These iterators will will keep producing values until you tell them to stop. + ```exercism/note To avoid infinite loops, you can use `break` to end a loop. ``` From 8a88a5520b51d2c2439bdad6f070495a6c35434f Mon Sep 17 00:00:00 2001 From: Carl Date: Sun, 25 Dec 2022 19:17:03 +0100 Subject: [PATCH 05/15] Started on itertools exercise --- concepts/itertools/about.md | 41 ++ exercises/concept/itertools/.docs/hints.md | 34 ++ .../concept/itertools/.docs/instructions.md | 115 ++++++ .../concept/itertools/.docs/introduction.md | 362 ++++++++++++++++++ exercises/concept/itertools/.meta/config.json | 25 ++ exercises/concept/itertools/.meta/design.md | 62 +++ exercises/concept/itertools/.meta/exemplar.py | 45 +++ .../concept/itertools/locomotive_engineer.py | 54 +++ .../itertools/locomotive_engineer_test.py | 90 +++++ 9 files changed, 828 insertions(+) create mode 100644 exercises/concept/itertools/.docs/hints.md create mode 100644 exercises/concept/itertools/.docs/instructions.md create mode 100644 exercises/concept/itertools/.docs/introduction.md create mode 100644 exercises/concept/itertools/.meta/config.json create mode 100644 exercises/concept/itertools/.meta/design.md create mode 100644 exercises/concept/itertools/.meta/exemplar.py create mode 100644 exercises/concept/itertools/locomotive_engineer.py create mode 100644 exercises/concept/itertools/locomotive_engineer_test.py diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index 366515cf1a..75070acfd6 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -1,3 +1,42 @@ +# About + +Itertools is a module in the Python standard library that provides a number of functions that create iterators for efficient looping. +Iterators are objects that can be used in for example a for loop. + +There are a number of functions in the itertools module that are useful for looping over data. +These functions often are also able to enchant the readability and maintainabillity of the code. + +This concept will cover these functions and how to use them: + +- `chain()` +- `chain.from_iterable()` +- `compress()` +- `islice()` +- `pairwise()` +- Maybe `tee()` +- `zip_longest()` +- `product()` +- `permutations()` +- `combinations()` +- `combinations_with_replacement()` +- `count()` +- `cycle()` +- `repeat()` + +There are more functions in the itertools module, like: + +- `accumulate()` +- `groupby()` +- `starmap()` +- `takewhile()` +- `dropwhile()` +- `filterfalse()` + +These functions will be coverd in a later concept. + +`count()`, `cycle()`, and`repeat()` is cataogorized as infinite iterators. +These iterators will never terminate and will keep looping forever. + ## Iterators terminating on the shortest input sequence ### Chain() @@ -338,3 +377,5 @@ Meaning that it is not an infinite loop if that parameter is given. ... 5 5 5 ``` + +[itertools]: https://docs.python.org/3/library/itertools.html diff --git a/exercises/concept/itertools/.docs/hints.md b/exercises/concept/itertools/.docs/hints.md new file mode 100644 index 0000000000..6bfae5f3e3 --- /dev/null +++ b/exercises/concept/itertools/.docs/hints.md @@ -0,0 +1,34 @@ +# Hints + +## General + +- To extract multiple arguments in the function parameters so can you pack them with the `*args` operator for `list` or `tuples` or `**kwargs` for keyword-based arguments. +- To pack or unpack use the `*` or `**` operator. + +## 1. Create a list of all wagons + +- Multiple arguments in the function parameters can be packed with the `*args` operator. + +## 2. Fix list of wagons + +- Using unpacking with the `*` operator, lets you extract the first two elements of a `list` while keeping the rest intact. +- To add another `list` into an existing `list`, you can use the `*` operator to "spread" the `list`. + +## 3. Add missing stops + +- Using `**kwargs` as a function parameter will allow an arbitrary amount of keyword arguments to be passed. +- Using `**` as an argument will unpack a dictionary into keyword arguments. +- You can put keyword arguments in a `{}` or `dict()`. +- To get the values out of a dictionary, you can use the `.values()` method. + +## 4. Extend routing information + +- Using `**` as an argument will unpack a dictionary into keyword arguments. +- You can put keyword arguments in a `{}` or `dict()`. + +## 5. Fix the wagon depot + +- `zip(*iterators)` can use used to transpose a nested `list`. +- To extract data from zipped iterators, you can use a for loop. +- you can also unpack zipped iterators using `*`. + `[*content] = zip(iterator_1, iterator_2)` will unzip the `tuple` produced by `zip()` into a `list`. diff --git a/exercises/concept/itertools/.docs/instructions.md b/exercises/concept/itertools/.docs/instructions.md new file mode 100644 index 0000000000..af914e4924 --- /dev/null +++ b/exercises/concept/itertools/.docs/instructions.md @@ -0,0 +1,115 @@ +# Instructions + +Your friend Linus is a Locomotive Engineer who drives cargo trains between cities. +Although they are amazing at handling trains, they are not amazing at handling logistics or computers. +They would like to enlist your programming help organizing train details and correcting mistakes in route data. + +```exercism/note +This exercise could easily be solved using slicing, indexing, and various `dict` methods. +However, we would like you to practice packing, unpacking, and multiple assignment in solving each of the tasks below. +``` + +## 1. Create a list of all wagons + +Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data packaged into a unified `list`. + +Implement a function `get_list_of_wagons()` that accepts an arbitrary number of wagon IDs. +Each ID will be a positive integer. +The function should then `return` the given IDs as a single `list`. + +```python +>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5) +[1, 7, 12, 3, 14, 8, 5] +``` + +## 2. Fix the list of wagons + +At this point, you are starting to get a feel for the data and how it's used in the logistics program. +The ID system always assigns the locomotive an ID of **1**, with the remainder of the wagons in the train assigned a randomly chosen ID greater than **1**. + +Your friend had to connect two new wagons to the train and forgot to update the system! +Now, the first two wagons in the train `list` have to be moved to the end, or everything will be out of order. + +To make matters more complicated, your friend just uncovered a second `list` that appears to contain missing wagon IDs. +All they can remember is that once the new wagons are moved, the IDs from this second `list` should be placed directly after the designated locomotive. + +Linus would be really grateful to you for fixing their mistakes and consolidating the data. + +Implement a function `fix_list_of_wagons()` that takes two `lists` containing wagon IDs. +It should reposition the first two items of the first `list` to the end, and insert the values from the second `list` behind (_on the right hand side of_) the locomotive ID (**1**). +The function should then `return` a `list` with the modifications. + +```python +>>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15]) +[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5] +``` + +## 3. Add missing stops + +Now that all the wagon data is correct, Linus would like you to update the system's routing information. +Along a transport route, a train might make stops at a few different stations to pick up and/or drop off cargo. +Each journey could have a different amount of these intermediary delivery points. +Your friend would like you to update the systems routing `dict` with any missing/additional delivery information. + +Implement a function `add_missing_stops()` that accepts a routing `dict` followed by a variable number of keyword arguments. +These arguments could be in the form of a `dict` holding one or more stops, or any number of `stop_number=city` keyword pairs. +Your function should then return the routing `dict` updated with an additional `key` that holds a `list` of all the added stops in order. + +```python +>>> add_missing_stops({"from": "New York", "to": "Miami"}, + stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta", + stop_4="Jacksonville", stop_5="Orlando") + +{"from": "New York", "to": "Miami", "stops": ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]} +``` + +## 4. Extend routing information + +Linus has been working on the routing program and has noticed that certain routes are missing some important details. +Initial route information has been constructed as a `dict` and your friend would like you to update that `dict` with whatever might be missing. +Every route in the system requires slightly different details, so Linus would really prefer a generic solution. + +Implement a function called `extend_route_information()` that accepts two `dicts`. +The first `dict` contains the origin and destination cities the train route runs between. + +The second `dict` contains other routing details such as train speed, length, or temperature. +The function should return a consolidated `dict` with all routing information. + +```exercism/note +The second `dict` can contain different/more properties than the ones shown in the example. +``` + +```python +>>> extend_route_information({"from": "Berlin", "to": "Hamburg"}, {"length": "100", "speed": "50"}) +{"from": "Berlin", "to": "Hamburg", "length": "100", "speed": "50"} +``` + +## 5. Fix the wagon depot + +When Linus was surveying the wagon depot they noticed that the wagons were not getting stored in the correct order. +In addition to an ID, each wagon has a color that corresponds to the type of cargo it carries. +Wagons are stored in the depot in grids, where each column in the grid has wagons of the same color. + +However, the logistics system shows `lists` of wagons to be stored in the depot have their _rows_ grouped by color. +But for the storage grid to work correctly, each _row_ should have three different colors so that the _columns_ align by color. +Your friend would like you to sort out the wagon depot `lists`, so that the wagons get stored correctly. + +Implement a function called `fix_wagon_depot()` that accepts a `list` of three items. +Each `list` item is a sublist (or "row") that contains three `tuples`. +Each `tuple` is a `(, )` pair. + +Your function should return a `list` with the three "row" `lists` reordered to have the wagons swapped into their correct positions. + +```python +>>> fix_wagon_depot([ + [(2, "red"), (4, "red"), (8, "red")], + [(5, "blue"), (9, "blue"), (13,"blue")], + [(3, "orange"), (7, "orange"), (11, "orange")], + ]) + +[ +[(2, "red"), (5, "blue"), (3, "orange")], +[(4, "red"), (9, "blue"), (7, "orange")], +[(8, "red"), (13,"blue"), (11, "orange")] +] +``` diff --git a/exercises/concept/itertools/.docs/introduction.md b/exercises/concept/itertools/.docs/introduction.md new file mode 100644 index 0000000000..e010c07576 --- /dev/null +++ b/exercises/concept/itertools/.docs/introduction.md @@ -0,0 +1,362 @@ +# Unpacking and Multiple Assignment + +Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration. +Unpacked values can then be assigned to variables within the same statement, which is commonly referred to as [Multiple assignment][multiple assignment]. + +The special operators `*` and `**` are often used in unpacking contexts and with multiple assignment. + +```exercism/caution +`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators. +``` + +## Multiple assignment + +In multiple assignment, the number of variables on the left side of the assignment operator (`=`) must match the number of values on the right side. +To separate the values, use a comma `,`: + +```python +>>> a, b = 1, 2 +>>> a +1 +``` + +If the multiple assignment gets an incorrect number of variables for the values given, a `ValueError` will be thrown: + +```python +>>> x, y, z = 1, 2 + +ValueError: too many values to unpack (expected 3, got 2) +``` + +Multiple assignment is not limited to one data type: + +```python +>>> x, y, z = 1, "Hello", True +>>> x +1 + +>>> y +'Hello' + +>>> z +True +``` + +Multiple assignment can be used to swap elements in `lists`. +This practice is pretty common in [sorting algorithms][sorting algorithms]. +For example: + +```python +>>> numbers = [1, 2] +>>> numbers[0], numbers[1] = numbers[1], numbers[0] +>>> numbers +[2, 1] +``` + +Since `tuples` are immutable, you can't swap elements in a `tuple`. + +## Unpacking + +```exercism/note +The examples below use `lists` but the same concepts apply to `tuples`. +``` + +In Python, it is possible to [unpack the elements of `list`/`tuple`/`dictionary`][unpacking] into distinct variables. +Since values appear within `lists`/`tuples` in a specific order, they are unpacked into variables in the same order: + +```python +>>> fruits = ["apple", "banana", "cherry"] +>>> x, y, z = fruits +>>> x +"apple" +``` + +If there are values that are not needed then you can use `_` to flag them: + +```python +>>> fruits = ["apple", "banana", "cherry"] +>>> _, _, z = fruits +>>> z +"cherry" +``` + +### Deep unpacking + +Unpacking and assigning values from a `list`/`tuple` inside of a `list` or `tuple` (_also known as nested lists/tuples_), works in the same way a shallow unpacking does, but often needs qualifiers to clarify the values context or position: + +```python +>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]] +>>> [[a, b], [c, d]] = fruits_vegetables +>>> a +"apple" + +>>> d +"potato" +``` + +You can also deeply unpack just a portion of a nested `list`/`tuple`: + +```python +>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]] +>>> [a, [c, d]] = fruits_vegetables +>>> a +["apple", "banana"] + +>>> c +"carrot" +``` + +If the unpacking has variables with incorrect placement and/or an incorrect number of values, you will get a `ValueError`: + +```python +>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]] +>>> [[a, b], [d]] = fruits_vegetables + +ValueError: too many values to unpack (expected 1) +``` + +### Unpacking a list/tuple with `*` + +When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the "leftover" values. +This is clearer than slicing the `list`/`tuple` (_which in some situations is less readable_). +For example, we can extract the first element and then assign the remaining values into a new `list` without the first element: + +```python +>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"] +>>> x, *last = fruits +>>> x +"apple" + +>>> last +["banana", "cherry", "orange", "kiwi", "melon", "mango"] +``` + +We can also extract the values at the beginning and end of the `list` while grouping all the values in the middle: + +```python +>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"] +>>> x, *middle, y, z = fruits +>>> y +"melon" + +>>> middle +["banana", "cherry", "orange", "kiwi"] +``` + +We can also use `*` in deep unpacking: + +```python +>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]] +>>> [[a, *rest], b] = fruits_vegetables +>>> a +"apple" + +>>> rest +["banana", "melon"] +``` + +### Unpacking a dictionary + +[Unpacking a dictionary][packing and unpacking] is a bit different than unpacking a `list`/`tuple`. +Iteration over dictionaries defaults to the **keys**. +So when unpacking a `dict`, you can only unpack the **keys** and not the **values**: + +```python +>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3} +>>> x, y, z = fruits_inventory +>>> x +"apple" +``` + +If you want to unpack the values then you can use the `values()` method: + +```python +>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3} +>>> x, y, z = fruits_inventory.values() +>>> x +6 +``` + +If both **keys** and **values** are needed, use the `items()` method. +Using `items()` will generate tuples with **key-value** pairs. +This is because of [`dict.items()` generates an iterable with key-value `tuples`][items]. + +```python +>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3} +>>> x, y, z = fruits_inventory.items() +>>> x +("apple", 6) +``` + +## Packing + +[Packing][packing and unpacking] is the ability to group multiple values into one `list` that is assigned to a variable. +This is useful when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable. +It also makes it possible to perform merges on 2 or more `lists`/`tuples`/`dicts`. + +### Packing a list/tuple with `*` + +Packing a `list`/`tuple` can be done using the `*` operator. +This will pack all the values into a `list`/`tuple`. + +```python +>>> fruits = ("apple", "banana", "cherry") +>>> more_fruits = ["orange", "kiwi", "melon", "mango"] + +# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits +>>> combined_fruits = *fruits, *more_fruits + +# If there is no * on to the left of the "=" the result is a tuple +>>> combined_fruits +("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango") + +# If the * operator is used on the left side of "=" the result is a list +>>> *combined_fruits_too, = *fruits, *more_fruits +>>> combined_fruits_too +['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango'] +``` + +### Packing a dictionary with `**` + +Packing a dictionary is done by using the `**` operator. +This will pack all **key**-**value** pairs from one dictionary into another dictionary, or combine two dictionaries together. + +```python +>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3} +>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3} + +# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined. +>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory} + +# then the pairs are packed into combined_fruits_inventory +>>> combined_fruits_inventory +{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3} +``` + +## Usage of `*` and `**` with functions + +### Packing with function parameters + +When you create a function that accepts an arbitrary number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] in the function definition. +`*args` is used to pack an arbitrary number of positional (non-keyworded) arguments and +`**kwargs` is used to pack an arbitrary number of keyword arguments. + +Usage of `*args`: + +```python +# This function is defined to take any number of positional arguments + +>>> def my_function(*args): +... print(args) + +# Arguments given to the function are packed into a tuple + +>>> my_function(1, 2, 3) +(1, 2, 3) + +>>> my_function("Hello") +("Hello") + +>>> my_function(1, 2, 3, "Hello", "Mars") +(1, 2, 3, "Hello", "Mars") +``` + +Usage of `**kwargs`: + +```python +# This function is defined to take any number of keyword arguments + +>>> def my_function(**kwargs): +... print(kwargs) + +# Arguments given to the function are packed into a dictionary + +>>> my_function(a=1, b=2, c=3) +{"a": 1, "b": 2, "c": 3} +``` + +`*args` and `**kwargs` can also be used in combination with one another: + +```python +>>> def my_function(*args, **kwargs): +... print(sum(args)) +... for key, value in kwargs.items(): +... print(str(key) + " = " + str(value)) + +>>> my_function(1, 2, 3, a=1, b=2, c=3) +6 +a = 1 +b = 2 +c = 3 +``` + +You can also write parameters before `*args` to allow for specific positional arguments. +Individual keyword arguments then have to appear before `**kwargs`. + +```exercism/caution +[Arguments have to be structured](https://www.python-engineer.com/courses/advancedpython/18-function-arguments/) like this: + +`def my_function(, *args, , **kwargs)` + +If you don't follow this order then you will get an error. +``` + +```python +>>> def my_function(a, b, *args): +... print(a) +... print(b) +... print(args) + +>>> my_function(1, 2, 3, 4, 5) +1 +2 +(3, 4, 5) +``` + +Writing arguments in an incorrect order will result in an error: + +```python +>>>def my_function(*args, a, b): +... print(args) + +>>>my_function(1, 2, 3, 4, 5) +Traceback (most recent call last): + File "c:\something.py", line 3, in + my_function(1, 2, 3, 4, 5) +TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b' +``` + +### Unpacking into function calls + +You can use `*` to unpack a `list`/`tuple` of arguments into a function call. +This is very useful for functions that don't accept an `iterable`: + +```python +>>> def my_function(a, b, c): +... print(c) +... print(b) +... print(a) + +numbers = [1, 2, 3] +>>> my_function(*numbers) +3 +2 +1 +``` + +Using `*` unpacking with the `zip()` function is another common use case. +Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the values from each `iterable` grouped: + +```python +>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True]) +>>> a, *rest = zip(*values) +>>> rest +[('y', 2, False), ('z', 3, True)] +``` + +[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/ +[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/ +[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/ +[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/ +[sorting algorithms]: https://realpython.com/sorting-algorithms-python/ +[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp diff --git a/exercises/concept/itertools/.meta/config.json b/exercises/concept/itertools/.meta/config.json new file mode 100644 index 0000000000..e9110e93a0 --- /dev/null +++ b/exercises/concept/itertools/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [ + "meatball133", + "BethanyG" + ], + "contributors": [ + "IsaacG" + ], + "files": { + "solution": [ + "locomotive_engineer.py" + ], + "test": [ + "locomotive_engineer_test.py" + ], + "exemplar": [ + ".meta/exemplar.py" + ] + }, + "forked_from": [ + "javascript/train-driver" + ], + "icon": "tracks-on-tracks-on-tracks", + "blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system." +} diff --git a/exercises/concept/itertools/.meta/design.md b/exercises/concept/itertools/.meta/design.md new file mode 100644 index 0000000000..933b7cb8ff --- /dev/null +++ b/exercises/concept/itertools/.meta/design.md @@ -0,0 +1,62 @@ +# Design + +## Goal + +This concept exercise is meant to teach an understanding/use of `unpacking` and the `*` (splat) and `**` (double splat) operators in Python. + +
+ +## Learning objectives + +- Understand/use `unpacking` through the use of `*` and `**` _prefix_ operators in various scenarios + - `*` and `**` as _prefixes_ ..... not to be confused with `*` (_multiply_) and `**` (_exponentiation_) as _infix_, or mathematical operators (**consider a link in the links doc or a mention in dig deeper.**) + - use in arguments to `functions` + - use in argument _capture_ for `functions` (_aka passing an arbitrary number of arguments -- *args * & \*\*kwargs_) + - use in iterable (_mainly `tuple` and `list`_) unpacking & packing + - use in `dict` unpacking & packing +- Understand/use `unpacking` via `multiple assignment` + - using `multiple assignment ` in place of `indexing` + - using `multiple assignment` + `*` in place of `slicing` + - unpacking plus "leftovers" via `*` +- Differences between straight `multiple assignment` and `*` & `**` +- Deep unpacking + +## Concepts + +- `unpacking` +- `unpacking generalizations` +- `multiple assignment` + +## Topics that are Out of scope + +- `classes` +- `comprehensions` +- `comprehensions` in `lambdas` +- `map()`, `filter()` or `functools.reduce()` in a `comprehension` +- `function-arguments` beyond explaining briefly how `*`, `**` work in function arguments. +- `functools` beyond `functools.reduce()`(_this will get its own exercise_) +- `generators` +- using an `assignment expression` or "walrus" operator (`:=`) alone or in a `lambda` + +## Prerequisites + +- `basics` +- `bools` +- `comparisons` +- `dicts` +- `lists` +- `numbers` +- `strings` +- `tuples` +- `loops` + +## Representer + +This exercise does not require any specific logic to be added to the [representer][representer] + +## Analyzer + +This exercise does not require any specific logic to be added to the [analyzer][analyzer]. + +[analyzer]: https://github.com/exercism/python-analyzer +[representer]: https://github.com/exercism/python-representer diff --git a/exercises/concept/itertools/.meta/exemplar.py b/exercises/concept/itertools/.meta/exemplar.py new file mode 100644 index 0000000000..31eeb77718 --- /dev/null +++ b/exercises/concept/itertools/.meta/exemplar.py @@ -0,0 +1,45 @@ +"""Functions which helps the locomotive engineer to keep track of the train.""" + +import itertools + +def ice_cream_combinations(flawors, scoops): + """Return a tuple of all possible combinations without repetition. + + :param flawors: list - aributary number of flawors. + :param scoops: int - number of scoops. + :return: tuple - tuple of all possible combinations. + """ + + return tuple(itertools.combinations(flawors, scoops)) + + +def compress(): + """Fix the list of wagons. + + :param each_wagons_id: list - the list of wagons. + :param missing_wagons: list - the list of missing wagons. + :return: list - list of wagons. + """ + pass + + +def zip_longest(route, **kwargs): + """Add missing stops to route dict. + + :param route: dict - the dict of routing information. + :param **kwargs: arbitrary number of stops. + :return: dict - updated route dictionary. + """ + + return {**route, "stops": list(kwargs.values())} + + +def product(route, more_route_information): + """Extend route information with more_route_information. + + :param route: dict - the route information. + :param more_route_information: dict - extra route information. + :return: dict - extended route information. + """ + + return {**route, **more_route_information} diff --git a/exercises/concept/itertools/locomotive_engineer.py b/exercises/concept/itertools/locomotive_engineer.py new file mode 100644 index 0000000000..d9291f65a3 --- /dev/null +++ b/exercises/concept/itertools/locomotive_engineer.py @@ -0,0 +1,54 @@ +"""Functions which helps the locomotive engineer to keep track of the train.""" + + +# TODO: define the 'get_list_of_wagons' function +def get_list_of_wagons(): + """Return a list of wagons. + + :param: arbitrary number of wagons. + :return: list - list of wagons. + """ + pass + + +# TODO: define the 'fixListOfWagons()' function +def fix_list_of_wagons(each_wagons_id, missing_wagons): + """Fix the list of wagons. + + :parm each_wagons_id: list - the list of wagons. + :parm missing_wagons: list - the list of missing wagons. + :return: list - list of wagons. + """ + pass + + +# TODO: define the 'add_missing_stops()' function +def add_missing_stops(): + """Add missing stops to route dict. + + :param route: dict - the dict of routing information. + :param: arbitrary number of stops. + :return: dict - updated route dictionary. + """ + pass + + +# TODO: define the 'extend_route_information()' function +def extend_route_information(route, more_route_information): + """Extend route information with more_route_information. + + :param route: dict - the route information. + :param more_route_information: dict - extra route information. + :return: dict - extended route information. + """ + pass + + +# TODO: define the 'fix_wagon_depot()' function +def fix_wagon_depot(wagons_rows): + """Fix the list of rows of wagons. + + :param wagons_rows: list[list[tuple]] - the list of rows of wagons. + :return: list[list[tuple]] - list of rows of wagons. + """ + pass diff --git a/exercises/concept/itertools/locomotive_engineer_test.py b/exercises/concept/itertools/locomotive_engineer_test.py new file mode 100644 index 0000000000..d3d3178fdf --- /dev/null +++ b/exercises/concept/itertools/locomotive_engineer_test.py @@ -0,0 +1,90 @@ +import unittest +import pytest +from locomotive_engineer import (get_list_of_wagons, + fix_list_of_wagons, + add_missing_stops, + extend_route_information, + fix_wagon_depot) + + +class LocomotiveEngineerTest(unittest.TestCase): + + @pytest.mark.task(taskno=1) + def test_get_list_of_wagons(self): + input_data = [(1,5,2,7,4), (1,5), (1,), (1,9,3), (1,10,6,3,9,8,4,14,24,7)] + output_data = [[1,5,2,7,4], [1,5], [1], [1,9,3], [1,10,6,3,9,8,4,14,24,7]] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different wagon list instead.' + self.assertEqual(get_list_of_wagons(*input_data), output_data, msg=error_msg) + + @pytest.mark.task(taskno=2) + def test_fix_list_of_wagons(self): + input_data = [([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15]), + ([3, 27, 1, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19], [8, 10, 5, 9, 36, 7, 20]), + ([4, 2, 1], [8, 6, 15]), + ([3, 14, 1, 25, 7, 19, 10], [8, 6, 4, 5, 9, 21, 2, 13]) + ] + output_data = [[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5], + [1, 8, 10, 5, 9, 36, 7, 20, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19, 3, 27], + [1, 8, 6, 15, 4, 2], + [1, 8, 6, 4, 5, 9, 21, 2, 13, 25, 7, 19, 10, 3, 14] + ] + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different wagon list instead.' + self.assertEqual(fix_list_of_wagons(input_data[0], input_data[1]), output_data, msg=error_msg) + + @pytest.mark.task(taskno=3) + def test_add_missing_stops(self): + input_data = (({'from': 'Berlin', 'to': 'Hamburg'}, {'stop_1': 'Lepzig', 'stop_2': 'Hannover', 'stop_3': 'Frankfurt'}), + ({'from': 'Paris', 'to': 'London'}, {'stop_1': 'Lille'}), + ({'from': 'New York', 'to': 'Philadelphia'},{}), + ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'stop_1': 'Kungsbacka', 'stop_2': 'Varberg', 'stop_3': 'Halmstad', 'stop_4': 'Angelholm', 'stop_5': 'Lund', 'stop_6': 'Malmo'}) + ) + output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'stops': ['Lepzig', 'Hannover', 'Frankfurt']}, + {'from': 'Paris', 'to': 'London', 'stops': ['Lille']}, + {'from': 'New York', 'to': 'Philadelphia', 'stops': []}, + {'from': 'Gothenburg', 'to': 'Copenhagen', 'stops': ['Kungsbacka', 'Varberg', 'Halmstad', 'Angelholm', 'Lund', 'Malmo']} + ] + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different set of stops instead.' + self.assertEqual(add_missing_stops(input_data[0], **input_data[1]), output_data, msg=error_msg) + + @pytest.mark.task(taskno=4) + def test_extend_route_information(self): + input_data = [({'from': 'Berlin', 'to': 'Hamburg'}, {'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'}), + ({'from': 'Paris', 'to': 'London'}, {'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'}), + ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'})] + output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'}, + {'from': 'Paris', 'to': 'London', 'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'}, + {'from': 'Gothenburg', 'to': 'Copenhagen', 'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'} + ] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different route dictionary instead.' + self.assertEqual(extend_route_information(input_data[0], input_data[1]), output_data, msg=error_msg) + + @pytest.mark.task(taskno=5) + def test_fix_wagon_depot(self): + input_data = ( + [[(2, 'red'), (4, 'red'), (8, 'red')], [(5, 'blue'), (9, 'blue'), (13, 'blue')], [(3, 'orange'), (7, 'orange'), (11, 'orange')]], + [[(6, 'blue'), (10, 'blue'), (14, 'blue')], [(7, 'red'), (4, 'red'), (2, 'red')], [(3, 'orange'), (11, 'orange'), (15, 'orange')]], + [[(7, 'pink'), (4, 'pink'), (2, 'pink')], [(10, 'green'), (6, 'green'), (14, 'green')], [(9, 'yellow'), (5, 'yellow'), (13, 'yellow')]], + [[(3, 'purple'), (11, 'purple'), (15, 'purple')], [(20, 'black'), (16, 'black'), (12, 'black')], [(19, 'white'), (17, 'white'), (18, 'white')]] + ) + + output_data = ( + [[(2, 'red'), (5, 'blue'), (3, 'orange')], [(4, 'red'), (9, 'blue'), (7, 'orange')], [(8, 'red'), (13, 'blue'), (11, 'orange')]], + [[(6, 'blue'), (7, 'red'), (3, 'orange')], [(10, 'blue'), (4, 'red'), (11, 'orange')], [(14, 'blue'), (2, 'red'), (15, 'orange')]], + [[(7, 'pink'), (10, 'green'), (9, 'yellow')], [(4, 'pink'), (6, 'green'), (5, 'yellow')], [(2, 'pink'), (14, 'green'), (13, 'yellow')]], + [[(3, 'purple'), (20, 'black'), (19, 'white')], [(11, 'purple'), (16, 'black'), (17, 'white')], [(15, 'purple'), (12, 'black'), (18, 'white')]] + ) + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different wagon depot list instead.' + self.assertEqual(fix_wagon_depot(input_data), output_data, msg=error_msg) From 0a42047fd07941623ac3eb654763d1aefb456c89 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 27 Dec 2022 00:38:29 +0100 Subject: [PATCH 06/15] Started on the exercise --- .../.docs/hints.md | 0 .../.docs/instructions.md | 10 +-- .../.docs/introduction.md | 0 .../.meta/config.json | 0 .../.meta/design.md | 0 .../concept/ice-cream-stand/.meta/exemplar.py | 46 ++++++++++ .../ice-cream-stand/ice_cream_stand.py | 31 +++++++ .../ice-cream-stand/ice_cream_stand_test.py | 83 +++++++++++++++++ exercises/concept/itertools/.meta/exemplar.py | 45 ---------- .../concept/itertools/locomotive_engineer.py | 54 ----------- .../itertools/locomotive_engineer_test.py | 90 ------------------- 11 files changed, 165 insertions(+), 194 deletions(-) rename exercises/concept/{itertools => ice-cream-stand}/.docs/hints.md (100%) rename exercises/concept/{itertools => ice-cream-stand}/.docs/instructions.md (91%) rename exercises/concept/{itertools => ice-cream-stand}/.docs/introduction.md (100%) rename exercises/concept/{itertools => ice-cream-stand}/.meta/config.json (100%) rename exercises/concept/{itertools => ice-cream-stand}/.meta/design.md (100%) create mode 100644 exercises/concept/ice-cream-stand/.meta/exemplar.py create mode 100644 exercises/concept/ice-cream-stand/ice_cream_stand.py create mode 100644 exercises/concept/ice-cream-stand/ice_cream_stand_test.py delete mode 100644 exercises/concept/itertools/.meta/exemplar.py delete mode 100644 exercises/concept/itertools/locomotive_engineer.py delete mode 100644 exercises/concept/itertools/locomotive_engineer_test.py diff --git a/exercises/concept/itertools/.docs/hints.md b/exercises/concept/ice-cream-stand/.docs/hints.md similarity index 100% rename from exercises/concept/itertools/.docs/hints.md rename to exercises/concept/ice-cream-stand/.docs/hints.md diff --git a/exercises/concept/itertools/.docs/instructions.md b/exercises/concept/ice-cream-stand/.docs/instructions.md similarity index 91% rename from exercises/concept/itertools/.docs/instructions.md rename to exercises/concept/ice-cream-stand/.docs/instructions.md index af914e4924..c61b0e1b12 100644 --- a/exercises/concept/itertools/.docs/instructions.md +++ b/exercises/concept/ice-cream-stand/.docs/instructions.md @@ -9,13 +9,13 @@ This exercise could easily be solved using slicing, indexing, and various `dict` However, we would like you to practice packing, unpacking, and multiple assignment in solving each of the tasks below. ``` -## 1. Create a list of all wagons +## 1. All flawors combinations -Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data packaged into a unified `list`. +The ice cream stand wants to know all of the combinations there are of thier flawors. +Each ice cream has a different amount of scoopes of flawors and you can't use the same flawor more than once. -Implement a function `get_list_of_wagons()` that accepts an arbitrary number of wagon IDs. -Each ID will be a positive integer. -The function should then `return` the given IDs as a single `list`. +Implement a function `flawor_combinations()` that takes a `tuple` with an arbitrary number of flawors and an `int` which says how many scoopes. +The function should then `return` all combinations in the form of a `tuple`. ```python >>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5) diff --git a/exercises/concept/itertools/.docs/introduction.md b/exercises/concept/ice-cream-stand/.docs/introduction.md similarity index 100% rename from exercises/concept/itertools/.docs/introduction.md rename to exercises/concept/ice-cream-stand/.docs/introduction.md diff --git a/exercises/concept/itertools/.meta/config.json b/exercises/concept/ice-cream-stand/.meta/config.json similarity index 100% rename from exercises/concept/itertools/.meta/config.json rename to exercises/concept/ice-cream-stand/.meta/config.json diff --git a/exercises/concept/itertools/.meta/design.md b/exercises/concept/ice-cream-stand/.meta/design.md similarity index 100% rename from exercises/concept/itertools/.meta/design.md rename to exercises/concept/ice-cream-stand/.meta/design.md diff --git a/exercises/concept/ice-cream-stand/.meta/exemplar.py b/exercises/concept/ice-cream-stand/.meta/exemplar.py new file mode 100644 index 0000000000..bf3bbdf389 --- /dev/null +++ b/exercises/concept/ice-cream-stand/.meta/exemplar.py @@ -0,0 +1,46 @@ +"""Functions for ice cream stand.""" + +import itertools + +def ice_cream_combinations(flawors, scoops): + """Return a tuple of all possible combinations without repetition. + + :param flawors: list - aributary number of flawors. + :param scoops: int - number of scoops. + :return: tuple - tuple of all possible combinations. + """ + + return tuple(itertools.combinations(flawors, scoops)) + + +def sprinkles(ice_creams, selector): + """Get the ice cream with sprinkles. + + :param ice_creams: list - ice_cream_orders. + :param selector: list - which ice creams that needs sprinkels. + :return: list - ice creams that needs sprinkles. + """ + return list(itertools.compress(ice_creams, selector)) + + +def fill_out_ice_cream_menu(flavors, toping, sprinkles): + """Fill out ice cream menu. + + :param flavors: tuple - ice cream flavors. + :param toping: tuple - ice cream topings. + :param sprinkles: tuple - ice cream sprinkles. + :return: list - ice cream menu filled out. + """ + return list(itertools.zip_longest(flavors, toping, sprinkles, fillvalue="None")) + + +#def product(route, more_route_information): + """Extend route information with more_route_information. + + :param route: dict - the route information. + :param more_route_information: dict - extra route information. + :return: dict - extended route information. + """ + + itertools.product(route, more_route_information) + \ No newline at end of file diff --git a/exercises/concept/ice-cream-stand/ice_cream_stand.py b/exercises/concept/ice-cream-stand/ice_cream_stand.py new file mode 100644 index 0000000000..362209229d --- /dev/null +++ b/exercises/concept/ice-cream-stand/ice_cream_stand.py @@ -0,0 +1,31 @@ +"""Functions for ice cream stand.""" + +def ice_cream_combinations(flawors, scoops): + """Return a tuple of all possible combinations without repetition. + + :param flawors: list - aributary number of flawors. + :param scoops: int - number of scoops. + :return: tuple - tuple of all possible combinations. + """ + pass + + +def sprinkles(ice_creams, selector): + """Get the ice cream with sprinkles. + + :param ice_creams: list - ice_cream_orders. + :param selector: list - which ice creams that needs sprinkels. + :return: list - ice creams that needs sprinkles. + """ + pass + + +def fill_out_ice_cream_menu(flavors, toping, sprinkles): + """Fill out ice cream menu. + + :param flavors: tuple - ice cream flavors. + :param toping: tuple - ice cream topings. + :param sprinkles: tuple - ice cream sprinkles. + :return: list - ice cream menu filled out. + """ + pass \ No newline at end of file diff --git a/exercises/concept/ice-cream-stand/ice_cream_stand_test.py b/exercises/concept/ice-cream-stand/ice_cream_stand_test.py new file mode 100644 index 0000000000..fa5fde24d1 --- /dev/null +++ b/exercises/concept/ice-cream-stand/ice_cream_stand_test.py @@ -0,0 +1,83 @@ +import unittest +import pytest +from ice_cream_stand import (ice_cream_combinations, sprinkles, fill_out_ice_cream_menu) + + +class IceCreamStandTest(unittest.TestCase): + + @pytest.mark.task(taskno=1) + def test_ice_cream_combinations(self): + input_data = [(['vanilla', 'chocolate', 'strawberry'], 2), + (['vanilla', 'chocolate', 'strawberry'], 1), + (['vanilla', 'chocolate', 'strawberry'], 3), + (['vanilla', 'chocolate', 'strawberry', 'mint'], 2), + (['vanilla', 'chocolate', 'strawberry', 'mint', 'raspberry', 'blueberry'], 4) + ] + output_data = [(('vanilla', 'chocolate'), ('vanilla', 'strawberry'), ('chocolate', 'strawberry')), + (('vanilla',), ('chocolate',), ('strawberry',)), + (('vanilla', 'chocolate', 'strawberry'),), + (('vanilla', 'chocolate'), ('vanilla', 'strawberry'), ('vanilla', 'mint'), ('chocolate', 'strawberry'), ('chocolate', 'mint'), ('strawberry', 'mint')), + ( + ('vanilla', 'chocolate', 'strawberry', 'mint'), + ('vanilla', 'chocolate', 'strawberry', 'raspberry'), + ('vanilla', 'chocolate', 'strawberry', 'blueberry'), + ('vanilla', 'chocolate', 'mint', 'raspberry'), + ('vanilla', 'chocolate', 'mint', 'blueberry'), + ('vanilla', 'chocolate', 'raspberry', 'blueberry'), + ('vanilla', 'strawberry', 'mint', 'raspberry'), + ('vanilla', 'strawberry', 'mint', 'blueberry'), + ('vanilla', 'strawberry', 'raspberry', 'blueberry'), + ('vanilla', 'mint', 'raspberry', 'blueberry'), + ('chocolate', 'strawberry', 'mint', 'raspberry'), + ('chocolate', 'strawberry', 'mint', 'blueberry'), + ('chocolate', 'strawberry', 'raspberry', 'blueberry'), + ('chocolate', 'mint', 'raspberry', 'blueberry'), + ('strawberry', 'mint', 'raspberry', 'blueberry') + )] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different wagon list instead.' + self.assertEqual(ice_cream_combinations(input_data[0], input_data[1]), output_data, msg=error_msg) + + + @pytest.mark.task(taskno=2) + def test_sprinkles(self): + input_data = [(['ice_cream_1', 'ice_cream_2', 'ice_cream_3'], [0, 1, 0]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3'], [1, 0, 1]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'], [1, 1, 0, 0, 1]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'], [0, 0, 0, 0, 0]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5', 'ice_cream_6', 'ice_cream_7'], [1, 1, 1, 1, 1, 0, 0]), + ] + output_data = [['ice_cream_2'], + ['ice_cream_1', 'ice_cream_3'], + ['ice_cream_1', 'ice_cream_2', 'ice_cream_5'], + [], + ['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'] + ] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different wagon list instead.' + self.assertEqual(sprinkles(input_data[0], input_data[1]), output_data, msg=error_msg) + + + @pytest.mark.task(taskno=3) + def test_fill_out_ice_cream_menu(self): + input_data = [(['ice_cream_1', 'ice_cream_2', 'ice_cream_3'], [0, 1, 0]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3'], [1, 0, 1]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'], [1, 1, 0, 0, 1]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'], [0, 0, 0, 0, 0]), + (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5', 'ice_cream_6', 'ice_cream_7'], [1, 1, 1, 1, 1, 0, 0]), + ] + output_data = [['ice_cream_2'], + ['ice_cream_1', 'ice_cream_3'], + ['ice_cream_1', 'ice_cream_2', 'ice_cream_5'], + [], + ['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'] + ] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): + error_msg=f'Expected: {output_data} but got a different wagon list instead.' + self.assertEqual(fill_out_ice_cream_menu(input_data[0], input_data[1], input_data[2]), output_data, msg=error_msg) diff --git a/exercises/concept/itertools/.meta/exemplar.py b/exercises/concept/itertools/.meta/exemplar.py deleted file mode 100644 index 31eeb77718..0000000000 --- a/exercises/concept/itertools/.meta/exemplar.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Functions which helps the locomotive engineer to keep track of the train.""" - -import itertools - -def ice_cream_combinations(flawors, scoops): - """Return a tuple of all possible combinations without repetition. - - :param flawors: list - aributary number of flawors. - :param scoops: int - number of scoops. - :return: tuple - tuple of all possible combinations. - """ - - return tuple(itertools.combinations(flawors, scoops)) - - -def compress(): - """Fix the list of wagons. - - :param each_wagons_id: list - the list of wagons. - :param missing_wagons: list - the list of missing wagons. - :return: list - list of wagons. - """ - pass - - -def zip_longest(route, **kwargs): - """Add missing stops to route dict. - - :param route: dict - the dict of routing information. - :param **kwargs: arbitrary number of stops. - :return: dict - updated route dictionary. - """ - - return {**route, "stops": list(kwargs.values())} - - -def product(route, more_route_information): - """Extend route information with more_route_information. - - :param route: dict - the route information. - :param more_route_information: dict - extra route information. - :return: dict - extended route information. - """ - - return {**route, **more_route_information} diff --git a/exercises/concept/itertools/locomotive_engineer.py b/exercises/concept/itertools/locomotive_engineer.py deleted file mode 100644 index d9291f65a3..0000000000 --- a/exercises/concept/itertools/locomotive_engineer.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Functions which helps the locomotive engineer to keep track of the train.""" - - -# TODO: define the 'get_list_of_wagons' function -def get_list_of_wagons(): - """Return a list of wagons. - - :param: arbitrary number of wagons. - :return: list - list of wagons. - """ - pass - - -# TODO: define the 'fixListOfWagons()' function -def fix_list_of_wagons(each_wagons_id, missing_wagons): - """Fix the list of wagons. - - :parm each_wagons_id: list - the list of wagons. - :parm missing_wagons: list - the list of missing wagons. - :return: list - list of wagons. - """ - pass - - -# TODO: define the 'add_missing_stops()' function -def add_missing_stops(): - """Add missing stops to route dict. - - :param route: dict - the dict of routing information. - :param: arbitrary number of stops. - :return: dict - updated route dictionary. - """ - pass - - -# TODO: define the 'extend_route_information()' function -def extend_route_information(route, more_route_information): - """Extend route information with more_route_information. - - :param route: dict - the route information. - :param more_route_information: dict - extra route information. - :return: dict - extended route information. - """ - pass - - -# TODO: define the 'fix_wagon_depot()' function -def fix_wagon_depot(wagons_rows): - """Fix the list of rows of wagons. - - :param wagons_rows: list[list[tuple]] - the list of rows of wagons. - :return: list[list[tuple]] - list of rows of wagons. - """ - pass diff --git a/exercises/concept/itertools/locomotive_engineer_test.py b/exercises/concept/itertools/locomotive_engineer_test.py deleted file mode 100644 index d3d3178fdf..0000000000 --- a/exercises/concept/itertools/locomotive_engineer_test.py +++ /dev/null @@ -1,90 +0,0 @@ -import unittest -import pytest -from locomotive_engineer import (get_list_of_wagons, - fix_list_of_wagons, - add_missing_stops, - extend_route_information, - fix_wagon_depot) - - -class LocomotiveEngineerTest(unittest.TestCase): - - @pytest.mark.task(taskno=1) - def test_get_list_of_wagons(self): - input_data = [(1,5,2,7,4), (1,5), (1,), (1,9,3), (1,10,6,3,9,8,4,14,24,7)] - output_data = [[1,5,2,7,4], [1,5], [1], [1,9,3], [1,10,6,3,9,8,4,14,24,7]] - - for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): - with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different wagon list instead.' - self.assertEqual(get_list_of_wagons(*input_data), output_data, msg=error_msg) - - @pytest.mark.task(taskno=2) - def test_fix_list_of_wagons(self): - input_data = [([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15]), - ([3, 27, 1, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19], [8, 10, 5, 9, 36, 7, 20]), - ([4, 2, 1], [8, 6, 15]), - ([3, 14, 1, 25, 7, 19, 10], [8, 6, 4, 5, 9, 21, 2, 13]) - ] - output_data = [[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5], - [1, 8, 10, 5, 9, 36, 7, 20, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19, 3, 27], - [1, 8, 6, 15, 4, 2], - [1, 8, 6, 4, 5, 9, 21, 2, 13, 25, 7, 19, 10, 3, 14] - ] - for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): - with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different wagon list instead.' - self.assertEqual(fix_list_of_wagons(input_data[0], input_data[1]), output_data, msg=error_msg) - - @pytest.mark.task(taskno=3) - def test_add_missing_stops(self): - input_data = (({'from': 'Berlin', 'to': 'Hamburg'}, {'stop_1': 'Lepzig', 'stop_2': 'Hannover', 'stop_3': 'Frankfurt'}), - ({'from': 'Paris', 'to': 'London'}, {'stop_1': 'Lille'}), - ({'from': 'New York', 'to': 'Philadelphia'},{}), - ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'stop_1': 'Kungsbacka', 'stop_2': 'Varberg', 'stop_3': 'Halmstad', 'stop_4': 'Angelholm', 'stop_5': 'Lund', 'stop_6': 'Malmo'}) - ) - output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'stops': ['Lepzig', 'Hannover', 'Frankfurt']}, - {'from': 'Paris', 'to': 'London', 'stops': ['Lille']}, - {'from': 'New York', 'to': 'Philadelphia', 'stops': []}, - {'from': 'Gothenburg', 'to': 'Copenhagen', 'stops': ['Kungsbacka', 'Varberg', 'Halmstad', 'Angelholm', 'Lund', 'Malmo']} - ] - for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): - with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different set of stops instead.' - self.assertEqual(add_missing_stops(input_data[0], **input_data[1]), output_data, msg=error_msg) - - @pytest.mark.task(taskno=4) - def test_extend_route_information(self): - input_data = [({'from': 'Berlin', 'to': 'Hamburg'}, {'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'}), - ({'from': 'Paris', 'to': 'London'}, {'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'}), - ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'})] - output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'}, - {'from': 'Paris', 'to': 'London', 'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'}, - {'from': 'Gothenburg', 'to': 'Copenhagen', 'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'} - ] - - for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): - with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different route dictionary instead.' - self.assertEqual(extend_route_information(input_data[0], input_data[1]), output_data, msg=error_msg) - - @pytest.mark.task(taskno=5) - def test_fix_wagon_depot(self): - input_data = ( - [[(2, 'red'), (4, 'red'), (8, 'red')], [(5, 'blue'), (9, 'blue'), (13, 'blue')], [(3, 'orange'), (7, 'orange'), (11, 'orange')]], - [[(6, 'blue'), (10, 'blue'), (14, 'blue')], [(7, 'red'), (4, 'red'), (2, 'red')], [(3, 'orange'), (11, 'orange'), (15, 'orange')]], - [[(7, 'pink'), (4, 'pink'), (2, 'pink')], [(10, 'green'), (6, 'green'), (14, 'green')], [(9, 'yellow'), (5, 'yellow'), (13, 'yellow')]], - [[(3, 'purple'), (11, 'purple'), (15, 'purple')], [(20, 'black'), (16, 'black'), (12, 'black')], [(19, 'white'), (17, 'white'), (18, 'white')]] - ) - - output_data = ( - [[(2, 'red'), (5, 'blue'), (3, 'orange')], [(4, 'red'), (9, 'blue'), (7, 'orange')], [(8, 'red'), (13, 'blue'), (11, 'orange')]], - [[(6, 'blue'), (7, 'red'), (3, 'orange')], [(10, 'blue'), (4, 'red'), (11, 'orange')], [(14, 'blue'), (2, 'red'), (15, 'orange')]], - [[(7, 'pink'), (10, 'green'), (9, 'yellow')], [(4, 'pink'), (6, 'green'), (5, 'yellow')], [(2, 'pink'), (14, 'green'), (13, 'yellow')]], - [[(3, 'purple'), (20, 'black'), (19, 'white')], [(11, 'purple'), (16, 'black'), (17, 'white')], [(15, 'purple'), (12, 'black'), (18, 'white')]] - ) - - for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): - with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different wagon depot list instead.' - self.assertEqual(fix_wagon_depot(input_data), output_data, msg=error_msg) From 3a5f8ad108903a71f7698b805322dc45d554963b Mon Sep 17 00:00:00 2001 From: Carl Date: Thu, 29 Dec 2022 17:41:13 +0100 Subject: [PATCH 07/15] More work on itertools --- .../ice-cream-stand/.docs/instructions.md | 127 ++++++------------ .../concept/ice-cream-stand/.meta/config.json | 12 +- .../ice-cream-stand/ice_cream_stand.py | 2 +- .../ice-cream-stand/ice_cream_stand_test.py | 27 ++-- 4 files changed, 61 insertions(+), 107 deletions(-) diff --git a/exercises/concept/ice-cream-stand/.docs/instructions.md b/exercises/concept/ice-cream-stand/.docs/instructions.md index c61b0e1b12..c867ec46b9 100644 --- a/exercises/concept/ice-cream-stand/.docs/instructions.md +++ b/exercises/concept/ice-cream-stand/.docs/instructions.md @@ -1,115 +1,76 @@ # Instructions -Your friend Linus is a Locomotive Engineer who drives cargo trains between cities. -Although they are amazing at handling trains, they are not amazing at handling logistics or computers. -They would like to enlist your programming help organizing train details and correcting mistakes in route data. +An ice cream stand in the neighborhood does a lot of manual work. +You have offered to help them with a program that can automate some of the work. ```exercism/note -This exercise could easily be solved using slicing, indexing, and various `dict` methods. -However, we would like you to practice packing, unpacking, and multiple assignment in solving each of the tasks below. +This exercise could be solved with a lot of different approaches. +However, we would like you to practice using methods from the `itertools` module. ``` ## 1. All flawors combinations -The ice cream stand wants to know all of the combinations there are of thier flawors. -Each ice cream has a different amount of scoopes of flawors and you can't use the same flawor more than once. +The ice cream stand wants to make an advertisement for how many different ice creams they can make. +They therefore want a program that can generate all the combinations of flawors they can make. +Each ice cream has a different amount of scoopes of flawors but you can't use the same flawor more than once. Implement a function `flawor_combinations()` that takes a `tuple` with an arbitrary number of flawors and an `int` which says how many scoopes. The function should then `return` all combinations in the form of a `tuple`. ```python ->>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5) -[1, 7, 12, 3, 14, 8, 5] +>>> flawors = ['vanilla', 'chocolate', 'strawberry'] +>>> scoops = 2 +>>> ice_cream_combinations(flawors, scoops) +(('vanilla', 'chocolate'), ('vanilla', 'strawberry'), ('chocolate', 'strawberry')) ``` -## 2. Fix the list of wagons +## 2. Sprinkels -At this point, you are starting to get a feel for the data and how it's used in the logistics program. -The ID system always assigns the locomotive an ID of **1**, with the remainder of the wagons in the train assigned a randomly chosen ID greater than **1**. +They also want a program to optimize the process of adding sprinkels to the ice cream. +Currently they have a `list` of all ice cream order numbers and another `list` with `booleans` that say if the ice cream should have sprinkels or not. +The order numbers and the `booleans` are in the same order. -Your friend had to connect two new wagons to the train and forgot to update the system! -Now, the first two wagons in the train `list` have to be moved to the end, or everything will be out of order. +The ice cream stand has a machine that takes a `list` of orders that should have sprinkels and adds sprinkels to them. +Therefore they want a program that takes away all the orders that should not have sprinkels. -To make matters more complicated, your friend just uncovered a second `list` that appears to contain missing wagon IDs. -All they can remember is that once the new wagons are moved, the IDs from this second `list` should be placed directly after the designated locomotive. - -Linus would be really grateful to you for fixing their mistakes and consolidating the data. - -Implement a function `fix_list_of_wagons()` that takes two `lists` containing wagon IDs. -It should reposition the first two items of the first `list` to the end, and insert the values from the second `list` behind (_on the right hand side of_) the locomotive ID (**1**). -The function should then `return` a `list` with the modifications. - -```python ->>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15]) -[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5] -``` - -## 3. Add missing stops - -Now that all the wagon data is correct, Linus would like you to update the system's routing information. -Along a transport route, a train might make stops at a few different stations to pick up and/or drop off cargo. -Each journey could have a different amount of these intermediary delivery points. -Your friend would like you to update the systems routing `dict` with any missing/additional delivery information. - -Implement a function `add_missing_stops()` that accepts a routing `dict` followed by a variable number of keyword arguments. -These arguments could be in the form of a `dict` holding one or more stops, or any number of `stop_number=city` keyword pairs. -Your function should then return the routing `dict` updated with an additional `key` that holds a `list` of all the added stops in order. +Implement a function `sprinkels()` that takes a `list` of order numbers and a `list` of `booleans` and returns a `list` of order numbers that should have sprinkels. ```python ->>> add_missing_stops({"from": "New York", "to": "Miami"}, - stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta", - stop_4="Jacksonville", stop_5="Orlando") - -{"from": "New York", "to": "Miami", "stops": ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]} +>>> ice_creams = ['ice_cream_1', 'ice_cream_2', 'ice_cream_3'] +>>> selector = [0, 1, 0] +>>> sprinkles(ice_creams, selector) +['ice_cream_2'] ``` -## 4. Extend routing information - -Linus has been working on the routing program and has noticed that certain routes are missing some important details. -Initial route information has been constructed as a `dict` and your friend would like you to update that `dict` with whatever might be missing. -Every route in the system requires slightly different details, so Linus would really prefer a generic solution. - -Implement a function called `extend_route_information()` that accepts two `dicts`. -The first `dict` contains the origin and destination cities the train route runs between. - -The second `dict` contains other routing details such as train speed, length, or temperature. -The function should return a consolidated `dict` with all routing information. +## 3. Fill out ice cream menu -```exercism/note -The second `dict` can contain different/more properties than the ones shown in the example. -``` +Currently the ice cream has to manualy write down the ice cream menu. +Since they often make new ice creams they want a program that can generate the menu for them. -```python ->>> extend_route_information({"from": "Berlin", "to": "Hamburg"}, {"length": "100", "speed": "50"}) -{"from": "Berlin", "to": "Hamburg", "length": "100", "speed": "50"} -``` +The menu is built up like this: -## 5. Fix the wagon depot +| Flavors | Toping | Sprinkels | +| ---------- | --------- | --------- | +| Strawberry | Cherry | Licorice | +| Choclate | Raspberry | Caramel | +| Mint | Blueberry | None | +| Vanilla | None | None | -When Linus was surveying the wagon depot they noticed that the wagons were not getting stored in the correct order. -In addition to an ID, each wagon has a color that corresponds to the type of cargo it carries. -Wagons are stored in the depot in grids, where each column in the grid has wagons of the same color. +The ice cream stand has a `tuple` with all the ice cream flavors, a `tuple` with all the topings and a `tuple` with all the sprinkels. +They have set it up so the `tuple` of ice cream flavors, topings and sprinkels are in the same order. -However, the logistics system shows `lists` of wagons to be stored in the depot have their _rows_ grouped by color. -But for the storage grid to work correctly, each _row_ should have three different colors so that the _columns_ align by color. -Your friend would like you to sort out the wagon depot `lists`, so that the wagons get stored correctly. +They want a program that takes **i**th index of the `tuple` of ice cream flavors, topings and sprinkels and returns a `tuple` with the ice cream menu. -Implement a function called `fix_wagon_depot()` that accepts a `list` of three items. -Each `list` item is a sublist (or "row") that contains three `tuples`. -Each `tuple` is a `(, )` pair. +All ice creams flavors doesn't have to have a toping or sprinkels. +If an ice cream doesn't have a toping or sprinkels the value should be `"None"`. -Your function should return a `list` with the three "row" `lists` reordered to have the wagons swapped into their correct positions. +Implement a function `fill_out_ice_cream_menu()` that accepts a `tuple` with ice cream flavors, a `tuple` with topings, and a `tuple` sprinkels. +The function should `return` a `list` of `tuples` with the ice cream menu. ```python ->>> fix_wagon_depot([ - [(2, "red"), (4, "red"), (8, "red")], - [(5, "blue"), (9, "blue"), (13,"blue")], - [(3, "orange"), (7, "orange"), (11, "orange")], - ]) - -[ -[(2, "red"), (5, "blue"), (3, "orange")], -[(4, "red"), (9, "blue"), (7, "orange")], -[(8, "red"), (13,"blue"), (11, "orange")] -] +>>> flavors = ('vanilla', 'chocolate', 'strawberry') +>>> topings = ('cherry', 'raspberry') +>>> sprinkels = ('licorice') +>>> fill_out_ice_cream_menu(flavors, topings, sprinkels) +[('vanilla', 'cherry', 'licorice'), ('chocolate', 'raspberry', 'None'), ('strawberry', 'None', 'None')] ``` diff --git a/exercises/concept/ice-cream-stand/.meta/config.json b/exercises/concept/ice-cream-stand/.meta/config.json index e9110e93a0..726df4c22e 100644 --- a/exercises/concept/ice-cream-stand/.meta/config.json +++ b/exercises/concept/ice-cream-stand/.meta/config.json @@ -3,23 +3,17 @@ "meatball133", "BethanyG" ], - "contributors": [ - "IsaacG" - ], "files": { "solution": [ - "locomotive_engineer.py" + "ice_cream_stand.py" ], "test": [ - "locomotive_engineer_test.py" + "ice_cream_stand_test.py" ], "exemplar": [ ".meta/exemplar.py" ] }, - "forked_from": [ - "javascript/train-driver" - ], - "icon": "tracks-on-tracks-on-tracks", + "icon": "custom-set", "blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system." } diff --git a/exercises/concept/ice-cream-stand/ice_cream_stand.py b/exercises/concept/ice-cream-stand/ice_cream_stand.py index 362209229d..82728303f8 100644 --- a/exercises/concept/ice-cream-stand/ice_cream_stand.py +++ b/exercises/concept/ice-cream-stand/ice_cream_stand.py @@ -26,6 +26,6 @@ def fill_out_ice_cream_menu(flavors, toping, sprinkles): :param flavors: tuple - ice cream flavors. :param toping: tuple - ice cream topings. :param sprinkles: tuple - ice cream sprinkles. - :return: list - ice cream menu filled out. + :return: list[tuple] - ice cream menu filled out. """ pass \ No newline at end of file diff --git a/exercises/concept/ice-cream-stand/ice_cream_stand_test.py b/exercises/concept/ice-cream-stand/ice_cream_stand_test.py index fa5fde24d1..db750943b5 100644 --- a/exercises/concept/ice-cream-stand/ice_cream_stand_test.py +++ b/exercises/concept/ice-cream-stand/ice_cream_stand_test.py @@ -37,7 +37,7 @@ def test_ice_cream_combinations(self): for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different wagon list instead.' + error_msg=f'Expected: {output_data} but got different result' self.assertEqual(ice_cream_combinations(input_data[0], input_data[1]), output_data, msg=error_msg) @@ -58,26 +58,25 @@ def test_sprinkles(self): for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different wagon list instead.' + error_msg=f'Expected: {output_data} but got different result' self.assertEqual(sprinkles(input_data[0], input_data[1]), output_data, msg=error_msg) @pytest.mark.task(taskno=3) def test_fill_out_ice_cream_menu(self): - input_data = [(['ice_cream_1', 'ice_cream_2', 'ice_cream_3'], [0, 1, 0]), - (['ice_cream_1', 'ice_cream_2', 'ice_cream_3'], [1, 0, 1]), - (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'], [1, 1, 0, 0, 1]), - (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'], [0, 0, 0, 0, 0]), - (['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5', 'ice_cream_6', 'ice_cream_7'], [1, 1, 1, 1, 1, 0, 0]), - ] - output_data = [['ice_cream_2'], - ['ice_cream_1', 'ice_cream_3'], - ['ice_cream_1', 'ice_cream_2', 'ice_cream_5'], - [], - ['ice_cream_1', 'ice_cream_2', 'ice_cream_3', 'ice_cream_4', 'ice_cream_5'] + input_data = ((('vanilla', 'chocolate', 'strawberry'), ('cherry', 'raspberry'), ('licorice',)), + (('strawberry', 'chocolate', 'vanilla'), ('cherry',), ('licorice', 'caramel')), + (('chocolate', 'vanilla', 'strawberry'), ('cherry', 'raspberry', 'blueberry'), ('licorice', 'caramel', 'chocolate')), + (('chocolate', 'vanilla', 'strawberry'), (), ()), + (('strawberry', 'choclate', 'mint', 'vanilla'),('cherry', 'raspberry', 'blueberry'), ('licorice', 'caramel'))) + output_data = [[('vanilla', 'cherry', 'licorice'), ('chocolate', 'raspberry', 'None'), ('strawberry', 'None', 'None')], + [('strawberry', 'cherry', 'licorice'), ('chocolate', 'None', 'caramel'), ('vanilla', 'None', 'None')], + [('chocolate', 'cherry', 'licorice'), ('vanilla', 'raspberry', 'caramel'), ('strawberry', 'blueberry', 'chocolate')], + [('chocolate', 'None', 'None'), ('vanilla', 'None', 'None'), ('strawberry', 'None', 'None')], + [('strawberry', 'cherry', 'licorice'), ('choclate', 'raspberry', 'caramel'), ('mint', 'blueberry', 'None'), ('vanilla', 'None', 'None')] ] for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data): - error_msg=f'Expected: {output_data} but got a different wagon list instead.' + error_msg=f'Expected: {output_data} but got different result' self.assertEqual(fill_out_ice_cream_menu(input_data[0], input_data[1], input_data[2]), output_data, msg=error_msg) From da0630aa0a551d18504af5cac9a9de8ad6c2cac6 Mon Sep 17 00:00:00 2001 From: Carl Date: Thu, 29 Dec 2022 17:43:52 +0100 Subject: [PATCH 08/15] Fixes --- config.json | 8 ++++++++ exercises/concept/ice-cream-stand/.meta/exemplar.py | 10 ---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/config.json b/config.json index b2222755eb..9aa48ceab1 100644 --- a/config.json +++ b/config.json @@ -136,6 +136,14 @@ "prerequisites": ["bools", "loops", "conditionals", "numbers"], "status": "beta" }, + { + "slug": "ice-cream-stand", + "name": "Ice Cream Stand", + "uuid": "121388ab-d86c-498d-928c-1fe536583e97", + "concepts": ["itertools"], + "prerequisites": ["tuples"], + "status": "beta" + }, { "slug": "inventory-management", "name": "Inventory Management", diff --git a/exercises/concept/ice-cream-stand/.meta/exemplar.py b/exercises/concept/ice-cream-stand/.meta/exemplar.py index bf3bbdf389..98cbb9f411 100644 --- a/exercises/concept/ice-cream-stand/.meta/exemplar.py +++ b/exercises/concept/ice-cream-stand/.meta/exemplar.py @@ -33,14 +33,4 @@ def fill_out_ice_cream_menu(flavors, toping, sprinkles): """ return list(itertools.zip_longest(flavors, toping, sprinkles, fillvalue="None")) - -#def product(route, more_route_information): - """Extend route information with more_route_information. - - :param route: dict - the route information. - :param more_route_information: dict - extra route information. - :return: dict - extended route information. - """ - - itertools.product(route, more_route_information) \ No newline at end of file From 2605588f1c2efa207f6fce8c3746925f989aa62e Mon Sep 17 00:00:00 2001 From: Carl Date: Thu, 29 Dec 2022 19:49:10 +0100 Subject: [PATCH 09/15] Fixed link and updated the concept --- concepts/itertools/about.md | 10 +++++++--- concepts/itertools/links.json | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index 75070acfd6..10a54bb2c9 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -1,10 +1,11 @@ # About Itertools is a module in the Python standard library that provides a number of functions that create iterators for efficient looping. -Iterators are objects that can be used in for example a for loop. +Iterators are objects that can be irretated over. +For example a `for in :` loop. There are a number of functions in the itertools module that are useful for looping over data. -These functions often are also able to enchant the readability and maintainabillity of the code. +These functions often are also able to enchant the readability and/or maintainabillity of the code. This concept will cover these functions and how to use them: @@ -71,7 +72,8 @@ Using `tuple()`: ### chain.from_iterable() -Works like chain but takes a single nested iterable, unpacking that iterable into individual iterables. +Works like chain but takes a single nested iterable +Then the method unpack that iterable into individual iterables. ```python >>> import itertools @@ -90,6 +92,7 @@ Works like chain but takes a single nested iterable, unpacking that iterable int `compress(iterable, selectors)` creates an iterator from the input iterable where the corresponding selector is `True`. The selector can be `True`/`False` or integers, where 0 is `False` and 1 is `True`. +You can read more about the behavior when [numbers act like booleans][numbers-as-boolean-values]. ```python >>> import itertools @@ -379,3 +382,4 @@ Meaning that it is not an infinite loop if that parameter is given. ``` [itertools]: https://docs.python.org/3/library/itertools.html +[numbers-as-boolean-values]: https://realpython.com/python-boolean/#numbers-as-boolean-values diff --git a/concepts/itertools/links.json b/concepts/itertools/links.json index 8a44906b3f..2751df8532 100644 --- a/concepts/itertools/links.json +++ b/concepts/itertools/links.json @@ -1,6 +1,6 @@ [ { - "url": "https://docs.python.org/3/library/itertools.htm", + "url": "https://docs.python.org/3/library/itertools.html", "description": "Offical Python documentation for the itertools module." }, { From 8817012527f81230ba45f3270e70f727eaf2272d Mon Sep 17 00:00:00 2001 From: Carl Date: Fri, 30 Dec 2022 14:27:19 +0100 Subject: [PATCH 10/15] A bunch of fixes and some additions --- concepts/itertools/about.md | 36 ++++----- concepts/itertools/links.json | 6 +- .../ice-cream-stand/.docs/instructions.md | 48 ++++++------ .../concept/ice-cream-stand/.meta/config.json | 2 +- .../concept/ice-cream-stand/.meta/design.md | 73 ++++++++++++------- .../concept/ice-cream-stand/.meta/exemplar.py | 10 +-- .../ice-cream-stand/ice_cream_stand.py | 8 +- 7 files changed, 100 insertions(+), 83 deletions(-) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index 10a54bb2c9..545f0a7d2f 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -1,11 +1,11 @@ # About Itertools is a module in the Python standard library that provides a number of functions that create iterators for efficient looping. -Iterators are objects that can be irretated over. +Iterators are objects that can be iterated over. For example a `for in :` loop. There are a number of functions in the itertools module that are useful for looping over data. -These functions often are also able to enchant the readability and/or maintainabillity of the code. +These functions often are also able to enchant the readability and/or maintainability of the code. This concept will cover these functions and how to use them: @@ -33,16 +33,16 @@ There are more functions in the itertools module, like: - `dropwhile()` - `filterfalse()` -These functions will be coverd in a later concept. +These functions will be covered in a later concept. -`count()`, `cycle()`, and`repeat()` is cataogorized as infinite iterators. +`count()`, `cycle()`, and`repeat()` is categorized as infinite iterators. These iterators will never terminate and will keep looping forever. ## Iterators terminating on the shortest input sequence ### Chain() -`chain(iterable1, iterable2...)` creates an irretator of values from the iterables in the order they are given. +`chain(iterable1, iterable2...)` creates an iterator of values from the iterables in the order they are given. ```python >>> import itertools @@ -72,7 +72,7 @@ Using `tuple()`: ### chain.from_iterable() -Works like chain but takes a single nested iterable +`chain.from_iterable()` works like `chain` but takes a single nested iterable. Then the method unpack that iterable into individual iterables. ```python @@ -131,7 +131,7 @@ e c If you are using the online editor then you don't need to worry about this. ``` -`Pairwise(iterable)` was intruduced in Python 3.10 and returns an iterator of overlapping pairs of values from the input iterable. +`Pairwise(iterable)` was introduced in Python 3.10 and returns an iterator of overlapping pairs of values from the input iterable. ```python >>> import itertools @@ -147,7 +147,7 @@ Talk with Bethany about ### Zip_longest() -#### Explaning zip +#### Explaining zip ```exercism/caution Pythons `zip()` function should not be confused with the zip compression format. @@ -172,10 +172,10 @@ If the iterables are not the same length, then the iterator will stop when the s [('x', 1, True),('y', 2, False)] ``` -#### Explaning zip_longest +#### Explaining zip_longest `zip_longest(iterator, )` is a function from the `itertools` module. -Unlink `zip()`, it will not stop when the shortest iterable is exhausted. +Unlike `zip()`, it will not stop when the shortest iterable is exhausted. If the iterables are not the same length, `fillvalue` will be used to pad missing values. By the default the `fillvalue` is `None`. @@ -244,10 +244,10 @@ You can also give it multiple iterables. ('A', 'x') ('A', 'y') ('B', 'x') ('B', 'y') ('C', 'x') ('C', 'y') ('D', 'x') ('D', 'y') ``` -Here is an example of doing it wihout `product()`. -It looks similliar to the last example but since we have two iterables we need to nest the for loops. -Even though the proudct is given repeat=1. -The reasson to why it is only 2 for loops earlier was because we only had one iterable. +Here is an example of doing it without `product()`. +It looks similar to the last example but since we have two iterables we need to nest the for loops. +Even though the product is given repeat=1. +The reason to why it is only 2 for loops earlier was because we only had one iterable. If we had two iterables and gave it repeat=2 we would need 4 for loops. Since 2 \* 2 = 4. @@ -262,7 +262,7 @@ Since 2 \* 2 = 4. ### Permutations() `permutations(iterable, )` creates an iterator of tuples. -It works like `product()` but it doesnt repeat values from a specific positoon from the iterable and can only take one iterable. +It works like `product()` but it doesn't repeat values from a specific position from the iterable and can only take one iterable. The "r" keyword argument can be used to specify the number of times the input iterables are repeated. By default the "r" keyword argument is None. If "r" is None then the length of the iterable is used. @@ -313,7 +313,7 @@ The difference between this and `combinations()` is that it can repeat values. ## Infinite iterators Most of iterator from the `itertools` module get exhausted after a time. -But there are some that are infinite, these are known as infinte iterators. +But there are some that are infinite, these are known as infinite iterators. These iterators will will keep producing values until you tell them to stop. ```exercism/note @@ -351,7 +351,7 @@ Giving `count()` a negative step size will produces values in a descending order ### Cycle() -`cycle(iterable)` produces all values from the iterable in an infinte loop. +`cycle(iterable)` produces all values from the iterable in an infinite loop. A `list`, `tuple`, `string`, `dict` or any other iterable can be used. ```python @@ -369,7 +369,7 @@ A B C A B C A B C A ### Repeat() -`repeat(object, )` produces the same value in an infinte loop. +`repeat(object, )` produces the same value in an infinite loop. Although if the optional times parameter is given, the value will produces that many times. Meaning that it is not an infinite loop if that parameter is given. diff --git a/concepts/itertools/links.json b/concepts/itertools/links.json index 2751df8532..72bb0e8f1c 100644 --- a/concepts/itertools/links.json +++ b/concepts/itertools/links.json @@ -1,11 +1,11 @@ [ { "url": "https://docs.python.org/3/library/itertools.html", - "description": "Offical Python documentation for the itertools module." + "description": "Official Python documentation for the itertools module." }, { - "url": "http://example.com/", - "description": "TODO: add new link (above) and write a short description here of the resource." + "url": "https://realpython.com/python-itertools/", + "description": "Real python, itertools in python" }, { "url": "http://example.com/", diff --git a/exercises/concept/ice-cream-stand/.docs/instructions.md b/exercises/concept/ice-cream-stand/.docs/instructions.md index c867ec46b9..8a469bf1c0 100644 --- a/exercises/concept/ice-cream-stand/.docs/instructions.md +++ b/exercises/concept/ice-cream-stand/.docs/instructions.md @@ -8,32 +8,32 @@ This exercise could be solved with a lot of different approaches. However, we would like you to practice using methods from the `itertools` module. ``` -## 1. All flawors combinations +## 1. All flavors combinations The ice cream stand wants to make an advertisement for how many different ice creams they can make. -They therefore want a program that can generate all the combinations of flawors they can make. -Each ice cream has a different amount of scoopes of flawors but you can't use the same flawor more than once. +They therefore want a program that can generate all the combinations of flavors they can make. +Each ice cream has a different amount of scoops of flavors but you can't use the same flavor more than once. -Implement a function `flawor_combinations()` that takes a `tuple` with an arbitrary number of flawors and an `int` which says how many scoopes. +Implement a function `ice_cream_combinations()` that takes a `tuple` with an arbitrary number of flavors and an `int` which says how many scoops. The function should then `return` all combinations in the form of a `tuple`. ```python ->>> flawors = ['vanilla', 'chocolate', 'strawberry'] +>>> flavors = ['vanilla', 'chocolate', 'strawberry'] >>> scoops = 2 ->>> ice_cream_combinations(flawors, scoops) +>>> ice_cream_combinations(flavors, scoops) (('vanilla', 'chocolate'), ('vanilla', 'strawberry'), ('chocolate', 'strawberry')) ``` -## 2. Sprinkels +## 2. Sprinkles -They also want a program to optimize the process of adding sprinkels to the ice cream. -Currently they have a `list` of all ice cream order numbers and another `list` with `booleans` that say if the ice cream should have sprinkels or not. +They also want a program to optimize the process of adding sprinkles to the ice cream. +Currently they have a `list` of all ice cream order numbers and another `list` with `booleans` that say if the ice cream should have sprinkles or not. The order numbers and the `booleans` are in the same order. -The ice cream stand has a machine that takes a `list` of orders that should have sprinkels and adds sprinkels to them. -Therefore they want a program that takes away all the orders that should not have sprinkels. +The ice cream stand has a machine that takes a `list` of orders that should have sprinkles and adds sprinkles to them. +Therefore they want a program that takes away all the orders that should not have sprinkles. -Implement a function `sprinkels()` that takes a `list` of order numbers and a `list` of `booleans` and returns a `list` of order numbers that should have sprinkels. +Implement a function `sprinkles()` that takes a `list` of order numbers and a `list` of `booleans` and returns a `list` of order numbers that should have sprinkles. ```python >>> ice_creams = ['ice_cream_1', 'ice_cream_2', 'ice_cream_3'] @@ -44,33 +44,33 @@ Implement a function `sprinkels()` that takes a `list` of order numbers and a `l ## 3. Fill out ice cream menu -Currently the ice cream has to manualy write down the ice cream menu. +Currently the ice cream has to manually write down the ice cream menu. Since they often make new ice creams they want a program that can generate the menu for them. The menu is built up like this: -| Flavors | Toping | Sprinkels | +| Flavors | Toping | Sprinkles | | ---------- | --------- | --------- | | Strawberry | Cherry | Licorice | -| Choclate | Raspberry | Caramel | +| Chocolate | Raspberry | Caramel | | Mint | Blueberry | None | | Vanilla | None | None | -The ice cream stand has a `tuple` with all the ice cream flavors, a `tuple` with all the topings and a `tuple` with all the sprinkels. -They have set it up so the `tuple` of ice cream flavors, topings and sprinkels are in the same order. +The ice cream stand has a `tuple` with all the ice cream flavors, a `tuple` with all the toppings and a `tuple` with all the sprinkles. +They have set it up so the `tuple` of ice cream flavors, toppings and sprinkles are in the same order. -They want a program that takes **i**th index of the `tuple` of ice cream flavors, topings and sprinkels and returns a `tuple` with the ice cream menu. +They want a program that takes **i**th index of the `tuple` of ice cream flavors, toppings and sprinkles and returns a `tuple` with the ice cream menu. -All ice creams flavors doesn't have to have a toping or sprinkels. -If an ice cream doesn't have a toping or sprinkels the value should be `"None"`. +All ice creams flavors doesn't have to have a toping or sprinkles. +If an ice cream doesn't have a toping or sprinkles the value should be `"None"`. -Implement a function `fill_out_ice_cream_menu()` that accepts a `tuple` with ice cream flavors, a `tuple` with topings, and a `tuple` sprinkels. +Implement a function `fill_out_ice_cream_menu()` that accepts a `tuple` with ice cream flavors, a `tuple` with toppings, and a `tuple` sprinkles. The function should `return` a `list` of `tuples` with the ice cream menu. ```python >>> flavors = ('vanilla', 'chocolate', 'strawberry') ->>> topings = ('cherry', 'raspberry') ->>> sprinkels = ('licorice') ->>> fill_out_ice_cream_menu(flavors, topings, sprinkels) +>>> toppings = ('cherry', 'raspberry') +>>> sprinkles = ('licorice') +>>> fill_out_ice_cream_menu(flavors, toppings, sprinkles) [('vanilla', 'cherry', 'licorice'), ('chocolate', 'raspberry', 'None'), ('strawberry', 'None', 'None')] ``` diff --git a/exercises/concept/ice-cream-stand/.meta/config.json b/exercises/concept/ice-cream-stand/.meta/config.json index 726df4c22e..d01c09fed3 100644 --- a/exercises/concept/ice-cream-stand/.meta/config.json +++ b/exercises/concept/ice-cream-stand/.meta/config.json @@ -15,5 +15,5 @@ ] }, "icon": "custom-set", - "blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system." + "blurb": "Learn about itertools while helping the local ice cream stand" } diff --git a/exercises/concept/ice-cream-stand/.meta/design.md b/exercises/concept/ice-cream-stand/.meta/design.md index 933b7cb8ff..375666d767 100644 --- a/exercises/concept/ice-cream-stand/.meta/design.md +++ b/exercises/concept/ice-cream-stand/.meta/design.md @@ -2,53 +2,70 @@ ## Goal -This concept exercise is meant to teach an understanding/use of `unpacking` and the `*` (splat) and `**` (double splat) operators in Python. - -
+The goal of the concept exercise described in this issue is to teach understanding/use of the itertools module in Python. ## Learning objectives -- Understand/use `unpacking` through the use of `*` and `**` _prefix_ operators in various scenarios - - `*` and `**` as _prefixes_ ..... not to be confused with `*` (_multiply_) and `**` (_exponentiation_) as _infix_, or mathematical operators (**consider a link in the links doc or a mention in dig deeper.**) - - use in arguments to `functions` - - use in argument _capture_ for `functions` (_aka passing an arbitrary number of arguments -- *args * & \*\*kwargs_) - - use in iterable (_mainly `tuple` and `list`_) unpacking & packing - - use in `dict` unpacking & packing -- Understand/use `unpacking` via `multiple assignment` - - using `multiple assignment ` in place of `indexing` - - using `multiple assignment` + `*` in place of `slicing` - - unpacking plus "leftovers" via `*` -- Differences between straight `multiple assignment` and `*` & `**` -- Deep unpacking +Learn more about iteration tools the Python Standard Library provides through the itertools module. + +Build and understanding of and use the following functions from the module, as well as practicing some of the recipes included: + +At least one of the infinite itertators: `count()`, `cycle()`, or `repeat()` + +- `accumulate()` +- `product()` +- `chain() & chain.from_iterable()` +- `groupby()` +- `islice()` +- `zip_longest() and the zip() built-in` +- `permutations()` +- `combinations()` ## Concepts -- `unpacking` -- `unpacking generalizations` -- `multiple assignment` +- iteration +- iterators +- itertools ## Topics that are Out of scope -- `classes` -- `comprehensions` +- `classes` & `class customization` beyond the use of the `itertools` methods. +- `class-inheritance` beyond what is needed to customize iteration using `itertools` +- `comprehensions` beyond what is needed to work with itertools - `comprehensions` in `lambdas` -- `map()`, `filter()` or `functools.reduce()` in a `comprehension` -- `function-arguments` beyond explaining briefly how `*`, `**` work in function arguments. -- `functools` beyond `functools.reduce()`(_this will get its own exercise_) -- `generators` -- using an `assignment expression` or "walrus" operator (`:=`) alone or in a `lambda` +- coroutines +- `decorators` beyond what is needed to work with `itertools` +- functions and higher-order functions beyond what might be needed to work with itertools +- `functools` and related `map`, `filter()` and `functools.reduce()`(they have their own exercise which is a prerequisite to this one) +- `generators` beyond what might be needed to work with itertools (they have their own exercise which is a prerequisite to this one) +- `lambdas` beyond what might be needed to work with `itertools` +- using an assignment expression or "walrus" operator (:=) +- class decorators +- enums ## Prerequisites - `basics` -- `bools` +- `booleans` - `comparisons` -- `dicts` +- `rich-comparisons` +- **dicts** +- **dict-methods** +- **functions** +- **functional tools** +- _generators_ +- **higher-order functions** +- **Identity methods is and is not** +- `iteration` - `lists` +- `list-methods` +- `loops` - `numbers` +- `sequences` +- **sets** - `strings` +- `string-methods` - `tuples` -- `loops` ## Representer diff --git a/exercises/concept/ice-cream-stand/.meta/exemplar.py b/exercises/concept/ice-cream-stand/.meta/exemplar.py index 98cbb9f411..38350415c5 100644 --- a/exercises/concept/ice-cream-stand/.meta/exemplar.py +++ b/exercises/concept/ice-cream-stand/.meta/exemplar.py @@ -2,22 +2,22 @@ import itertools -def ice_cream_combinations(flawors, scoops): +def ice_cream_combinations(flavors, scoops): """Return a tuple of all possible combinations without repetition. - :param flawors: list - aributary number of flawors. + :param flavors: list - arbitrary number of flavors. :param scoops: int - number of scoops. :return: tuple - tuple of all possible combinations. """ - return tuple(itertools.combinations(flawors, scoops)) + return tuple(itertools.combinations(flavors, scoops)) def sprinkles(ice_creams, selector): """Get the ice cream with sprinkles. :param ice_creams: list - ice_cream_orders. - :param selector: list - which ice creams that needs sprinkels. + :param selector: list - which ice creams that needs sprinkles. :return: list - ice creams that needs sprinkles. """ return list(itertools.compress(ice_creams, selector)) @@ -27,7 +27,7 @@ def fill_out_ice_cream_menu(flavors, toping, sprinkles): """Fill out ice cream menu. :param flavors: tuple - ice cream flavors. - :param toping: tuple - ice cream topings. + :param toping: tuple - ice cream toppings. :param sprinkles: tuple - ice cream sprinkles. :return: list - ice cream menu filled out. """ diff --git a/exercises/concept/ice-cream-stand/ice_cream_stand.py b/exercises/concept/ice-cream-stand/ice_cream_stand.py index 82728303f8..cdbdfef829 100644 --- a/exercises/concept/ice-cream-stand/ice_cream_stand.py +++ b/exercises/concept/ice-cream-stand/ice_cream_stand.py @@ -1,9 +1,9 @@ """Functions for ice cream stand.""" -def ice_cream_combinations(flawors, scoops): +def ice_cream_combinations(flavors, scoops): """Return a tuple of all possible combinations without repetition. - :param flawors: list - aributary number of flawors. + :param flavors: list - arbitrary number of flavors. :param scoops: int - number of scoops. :return: tuple - tuple of all possible combinations. """ @@ -14,7 +14,7 @@ def sprinkles(ice_creams, selector): """Get the ice cream with sprinkles. :param ice_creams: list - ice_cream_orders. - :param selector: list - which ice creams that needs sprinkels. + :param selector: list - which ice creams that needs sprinkles. :return: list - ice creams that needs sprinkles. """ pass @@ -24,7 +24,7 @@ def fill_out_ice_cream_menu(flavors, toping, sprinkles): """Fill out ice cream menu. :param flavors: tuple - ice cream flavors. - :param toping: tuple - ice cream topings. + :param toping: tuple - ice cream toppings. :param sprinkles: tuple - ice cream sprinkles. :return: list[tuple] - ice cream menu filled out. """ From c30bce06fcf9ce69cce3f60d828346f0036add9c Mon Sep 17 00:00:00 2001 From: Carl Date: Fri, 30 Dec 2022 23:22:53 +0100 Subject: [PATCH 11/15] fixes and extended the chain() explanation --- concepts/itertools/about.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index 545f0a7d2f..8a9024f493 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -2,7 +2,7 @@ Itertools is a module in the Python standard library that provides a number of functions that create iterators for efficient looping. Iterators are objects that can be iterated over. -For example a `for in :` loop. +An example when you iterate over an iterator is a `for in :` loop. There are a number of functions in the itertools module that are useful for looping over data. These functions often are also able to enchant the readability and/or maintainability of the code. @@ -35,7 +35,7 @@ There are more functions in the itertools module, like: These functions will be covered in a later concept. -`count()`, `cycle()`, and`repeat()` is categorized as infinite iterators. +`count()`, `cycle()`, and `repeat()` is categorized as infinite iterators. These iterators will never terminate and will keep looping forever. ## Iterators terminating on the shortest input sequence @@ -52,6 +52,17 @@ These iterators will never terminate and will keep looping forever. 1 2 3 4 5 6 7 8 9 ``` +Since `chain()` takes iterables as arguments, so can it take it for example take: `set`, `tuple`, `list`, `str`, and more. +You can give iterables of different types at the same time. + +```python +>>> import itertools +>>> for number in itertools.chain([1, 2, 3], (4, 5, 6), {7, 8, 9}, "abc"): +... print(number, end=' ') +... +1 2 3 4 5 6 7 8 9 a b c +``` + Chain can also be used to concate a different amount of iterables to a list or tuple by using the `list()` or `tuple()` function. Using `list()`: From a4136f7dbf8b9a2022e005712c1604a7de54f674 Mon Sep 17 00:00:00 2001 From: Carl Date: Sat, 31 Dec 2022 10:51:14 +0100 Subject: [PATCH 12/15] extended description for combinations-rep, added hints, fixes --- concepts/itertools/about.md | 14 ++++---- .../concept/ice-cream-stand/.docs/hints.md | 33 +++++-------------- 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index 8a9024f493..fa9073bf4f 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -197,7 +197,7 @@ By the default the `fillvalue` is `None`. [('x', 1, True),('y', 2, False), ('z', 3, None), (None, 4, None)] ``` -An example where a fillvalue is given: +An example where a `fillvalue` is given: ```python >>> import itertools @@ -274,9 +274,9 @@ Since 2 \* 2 = 4. `permutations(iterable, )` creates an iterator of tuples. It works like `product()` but it doesn't repeat values from a specific position from the iterable and can only take one iterable. -The "r" keyword argument can be used to specify the number of times the input iterables are repeated. -By default the "r" keyword argument is None. -If "r" is None then the length of the iterable is used. +The **r** keyword argument can be used to specify the number of times the input iterables are repeated. +By default the **r** keyword argument is None. +If **r** is None then the length of the iterable is used. ```python >>> import itertools @@ -297,7 +297,7 @@ If "r" is None then the length of the iterable is used. ### Combinations() `combinations(iterable, r)` finds all the possible combinations of the given iterable. -The r keyword argument is used to specify the length of the tuples generated. +The **r** keyword argument is used to specify the length of the tuples generated. ```python >>> import itertools @@ -310,8 +310,10 @@ The r keyword argument is used to specify the length of the tuples generated. ### Combinations_with_replacement() `combinations_with_replacement(iterable, r)` finds all the possible combinations of the given iterable. -The r keyword argument is used to specify the length of the tuples generated. +The **r** keyword argument is used to specify the length of the tuples generated. The difference between this and `combinations()` is that it can repeat values. +That means that if you have "AB" and you want to find all the combinations of length 2 you will get `("A", "A"), ("A", "B"), ("B", "B")`. +While with `combinations()` you would only get `("A", "B")`. ```python >>> import itertools diff --git a/exercises/concept/ice-cream-stand/.docs/hints.md b/exercises/concept/ice-cream-stand/.docs/hints.md index 6bfae5f3e3..4b90eab35e 100644 --- a/exercises/concept/ice-cream-stand/.docs/hints.md +++ b/exercises/concept/ice-cream-stand/.docs/hints.md @@ -2,33 +2,18 @@ ## General -- To extract multiple arguments in the function parameters so can you pack them with the `*args` operator for `list` or `tuples` or `**kwargs` for keyword-based arguments. -- To pack or unpack use the `*` or `**` operator. +- All of the problems is solvable with different methods from the `itertools` module. -## 1. Create a list of all wagons +## 1. ice_cream_combinations -- Multiple arguments in the function parameters can be packed with the `*args` operator. +- The `itertools.combinations(iterable, size)` method can be used to generate all combinations of a an iterable like a `tuple` or `list`. -## 2. Fix list of wagons +## 2. sprinkles -- Using unpacking with the `*` operator, lets you extract the first two elements of a `list` while keeping the rest intact. -- To add another `list` into an existing `list`, you can use the `*` operator to "spread" the `list`. +- The `itertools.compress(iterable, selector)` method can be used to filter an iterable based on a boolean selector. -## 3. Add missing stops +## 3. fill_out_ice_cream_menu -- Using `**kwargs` as a function parameter will allow an arbitrary amount of keyword arguments to be passed. -- Using `**` as an argument will unpack a dictionary into keyword arguments. -- You can put keyword arguments in a `{}` or `dict()`. -- To get the values out of a dictionary, you can use the `.values()` method. - -## 4. Extend routing information - -- Using `**` as an argument will unpack a dictionary into keyword arguments. -- You can put keyword arguments in a `{}` or `dict()`. - -## 5. Fix the wagon depot - -- `zip(*iterators)` can use used to transpose a nested `list`. -- To extract data from zipped iterators, you can use a for loop. -- you can also unpack zipped iterators using `*`. - `[*content] = zip(iterator_1, iterator_2)` will unzip the `tuple` produced by `zip()` into a `list`. +- You can use `zip()` to combine an arbitrary number of iterables into a `tuple` of `tuples`. +- The `itertools.zip_longest(iterable, fillvalue)` method can be used to combine iterables of different lengths. +- You can give a value to the `fillvalue` parameter to fill in missing values. From 75ff2237b74d34d635cb78c0692fd42e88c7b524 Mon Sep 17 00:00:00 2001 From: Carl Date: Sat, 31 Dec 2022 10:53:01 +0100 Subject: [PATCH 13/15] Added link --- concepts/itertools/links.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/concepts/itertools/links.json b/concepts/itertools/links.json index 72bb0e8f1c..f925725ffb 100644 --- a/concepts/itertools/links.json +++ b/concepts/itertools/links.json @@ -8,8 +8,8 @@ "description": "Real python, itertools in python" }, { - "url": "http://example.com/", - "description": "TODO: add new link (above) and write a short description here of the resource." + "url": "https://www.geeksforgeeks.org/python-itertools/", + "description": "Geeks of geeks, python itertools" }, { "url": "http://example.com/", From 22eb91fc72ff58af2976f9bc3a182df26b963794 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Tue, 11 Jul 2023 23:17:15 -0700 Subject: [PATCH 14/15] Apply suggestions from code review Committing changes in prep for second round of revisions. --- concepts/itertools/about.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md index fa9073bf4f..8a3ae2bad8 100644 --- a/concepts/itertools/about.md +++ b/concepts/itertools/about.md @@ -1,41 +1,49 @@ # About -Itertools is a module in the Python standard library that provides a number of functions that create iterators for efficient looping. -Iterators are objects that can be iterated over. -An example when you iterate over an iterator is a `for in :` loop. +[`itertools`][itertools] is a module in the Python standard library that provides a number of functions that create iterators for more efficient looping. -There are a number of functions in the itertools module that are useful for looping over data. -These functions often are also able to enchant the readability and/or maintainability of the code. +[Iterators][iterators] are objects that represent a stream of data. + +An example is the "stream" of data when you iterate over a `list` in a `for in :` loop. + + +Many functions in `itertools` are very useful for looping/iterating over data streams in various ways. + +These functions often enhance the readability and/or maintainability of the code. + + +This concept will cover a selection of these functions and how to use them: -This concept will cover these functions and how to use them: - `chain()` - `chain.from_iterable()` - `compress()` - `islice()` - `pairwise()` -- Maybe `tee()` - `zip_longest()` - `product()` - `permutations()` - `combinations()` -- `combinations_with_replacement()` - `count()` - `cycle()` -- `repeat()` There are more functions in the itertools module, like: - `accumulate()` +-`combinations_with_replacement()` - `groupby()` + +- `repeat()` - `starmap()` + - `takewhile()` - `dropwhile()` - `filterfalse()` These functions will be covered in a later concept. -`count()`, `cycle()`, and `repeat()` is categorized as infinite iterators. +`count()`, `cycle()`, and `repeat()` are categorized as infinite iterators. + These iterators will never terminate and will keep looping forever. ## Iterators terminating on the shortest input sequence From 65901d0f967023d30ebaf5911ee8c85a222c08c0 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Tue, 11 Jul 2023 23:18:25 -0700 Subject: [PATCH 15/15] Update config.json --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 9aa48ceab1..1546fbfd9f 100644 --- a/config.json +++ b/config.json @@ -142,7 +142,7 @@ "uuid": "121388ab-d86c-498d-928c-1fe536583e97", "concepts": ["itertools"], "prerequisites": ["tuples"], - "status": "beta" + "status": "wip" }, { "slug": "inventory-management",