├── .gitignore ├── .replit ├── README.md ├── arithmetic_arranger.py ├── main.py └── test_module.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/python 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 4 | 5 | ### Python ### 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | pip-wheel-metadata/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | pytestdebug.log 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | doc/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # poetry 100 | #poetry.lock 101 | 102 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 103 | __pypackages__/ 104 | 105 | # Celery stuff 106 | celerybeat-schedule 107 | celerybeat.pid 108 | 109 | # SageMath parsed files 110 | *.sage.py 111 | 112 | # Environments 113 | # .env 114 | .env/ 115 | .venv/ 116 | env/ 117 | venv/ 118 | ENV/ 119 | env.bak/ 120 | venv.bak/ 121 | pythonenv* 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # operating system-related files 145 | *.DS_Store #file properties cache/storage on macOS 146 | Thumbs.db #thumbnail cache on Windows 147 | 148 | # profiling data 149 | .prof 150 | 151 | 152 | # End of https://www.toptal.com/developers/gitignore/api/python 153 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "python3" 2 | run = "python main.py" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Assignment 2 | 3 | Students in primary school often arrange arithmetic problems vertically to make them easier to solve. For example, "235 + 52" becomes: 4 | ``` 5 | 235 6 | + 52 7 | ----- 8 | ``` 9 | 10 | Create a function that receives a list of strings that are arithmetic problems and returns the problems arranged vertically and side-by-side. The function should optionally take a second argument. When the second argument is set to `True`, the answers should be displayed. 11 | 12 | ### For example 13 | 14 | Function Call: 15 | ```py 16 | arithmetic_arranger(["32 + 698", "3801 - 2", "45 + 43", "123 + 49"]) 17 | ``` 18 | 19 | Output: 20 | ``` 21 | 32 3801 45 123 22 | + 698 - 2 + 43 + 49 23 | ----- ------ ---- ----- 24 | ``` 25 | 26 | Function Call: 27 | ```py 28 | arithmetic_arranger(["32 + 8", "1 - 3801", "9999 + 9999", "523 - 49"], True) 29 | ``` 30 | 31 | Output: 32 | ``` 33 | 32 1 9999 523 34 | + 8 - 3801 + 9999 - 49 35 | ---- ------ ------ ----- 36 | 40 -3800 19998 474 37 | ``` 38 | 39 | ### Rules 40 | 41 | The function will return the correct conversion if the supplied problems are properly formatted, otherwise, it will **return** a **string** that describes an error that is meaningful to the user. 42 | 43 | 44 | * Situations that will return an error: 45 | * If there are **too many problems** supplied to the function. The limit is **five**, anything more will return: 46 | `Error: Too many problems.` 47 | * The appropriate operators the function will accept are **addition** and **subtraction**. Multiplication and division will return an error. Other operators not mentioned in this bullet point will not need to be tested. The error returned will be: 48 | `Error: Operator must be '+' or '-'.` 49 | * Each number (operand) should only contain digits. Otherwise, the function will return: 50 | `Error: Numbers must only contain digits.` 51 | * Each operand (aka number on each side of the operator) has a max of four digits in width. Otherwise, the error string returned will be: 52 | `Error: Numbers cannot be more than four digits.` 53 | * If the user supplied the correct format of problems, the conversion you return will follow these rules: 54 | * There should be a single space between the operator and the longest of the two operands, the operator will be on the same line as the second operand, both operands will be in the same order as provided (the first will be the top one and the second will be the bottom. 55 | * Numbers should be right-aligned. 56 | * There should be four spaces between each problem. 57 | * There should be dashes at the bottom of each problem. The dashes should run along the entire length of each problem individually. (The example above shows what this should look like.) 58 | 59 | ### Development 60 | 61 | Write your code in `arithmetic_arranger.py`. For development, you can use `main.py` to test your `arithmetic_arranger()` function. Click the "run" button and `main.py` will run. 62 | 63 | ### Testing 64 | 65 | The unit tests for this project are in `test_module.py`. We imported the tests from `test_module.py` to `main.py` for your convenience. The tests will run automatically whenever you hit the "run" button. 66 | 67 | ### Submitting 68 | 69 | Copy your project's URL and submit it to freeCodeCamp. 70 | -------------------------------------------------------------------------------- /arithmetic_arranger.py: -------------------------------------------------------------------------------- 1 | def arithmetic_arranger(problems, val=False): 2 | arranged_problems = '' 3 | if len(problems) > 5: 4 | arranged_problems = "Error: Too many problems." 5 | return arranged_problems 6 | 7 | # list of all operations in str format 8 | operations = list(map(lambda x: x.split()[1], problems)) 9 | if set(operations) != {'+', '-'} and len(set(operations)) != 2: 10 | arranged_problems = "Error: Operator must be '+' or '-'." 11 | return arranged_problems 12 | 13 | numbers = [] # list of all operands in str format 14 | for i in problems: 15 | p = i.split() 16 | numbers.extend([p[0], p[2]]) 17 | 18 | if not all(map(lambda x: x.isdigit(), numbers)): 19 | arranged_problems = "Error: Numbers must only contain digits." 20 | return arranged_problems 21 | 22 | if not all(map(lambda x: len(x) < 5, numbers)): 23 | arranged_problems = "Error: Numbers cannot be more than four digits." 24 | return arranged_problems 25 | 26 | top_row = '' 27 | dashes = '' 28 | values = list(map(lambda x: eval(x), problems)) 29 | solutions = '' 30 | for i in range(0, len(numbers), 2): 31 | space_width = max(len(numbers[i]), len(numbers[i+1])) + 2 32 | top_row += numbers[i].rjust(space_width) 33 | dashes += '-' * space_width 34 | solutions += str(values[i // 2]).rjust(space_width) 35 | if i != len(numbers) - 2: 36 | top_row += ' ' * 4 37 | dashes += ' ' * 4 38 | solutions += ' ' * 4 39 | 40 | bottom_row = '' 41 | for i in range(1, len(numbers), 2): 42 | space_width = max(len(numbers[i - 1]), len(numbers[i])) + 1 43 | bottom_row += operations[i // 2] 44 | bottom_row += numbers[i].rjust(space_width) 45 | if i != len(numbers) - 1: 46 | bottom_row += ' ' * 4 47 | 48 | if val: 49 | arranged_problems = '\n'.join((top_row, bottom_row, dashes, solutions)) 50 | else: 51 | arranged_problems = '\n'.join((top_row, bottom_row, dashes)) 52 | return arranged_problems 53 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # This entrypoint file to be used in development. Start by reading README.md 2 | from arithmetic_arranger import arithmetic_arranger 3 | from unittest import main 4 | 5 | 6 | print(arithmetic_arranger(["32 + 698", "3801 - 2", "45 + 43", "123 + 49"])) 7 | 8 | 9 | # Run unit tests automatically 10 | main(module='test_module', exit=False) -------------------------------------------------------------------------------- /test_module.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from arithmetic_arranger import arithmetic_arranger 3 | 4 | 5 | # the test case 6 | class UnitTests(unittest.TestCase): 7 | def test_arrangement(self): 8 | actual = arithmetic_arranger(["3 + 855", "3801 - 2", "45 + 43", "123 + 49"]) 9 | expected = " 3 3801 45 123\n+ 855 - 2 + 43 + 49\n----- ------ ---- -----" 10 | self.assertEqual(actual, expected, 'Expected different output when calling "arithmetic_arranger()" with ["3 + 855", "3801 - 2", "45 + 43", "123 + 49"]') 11 | 12 | actual = arithmetic_arranger(["11 + 4", "3801 - 2999", "1 + 2", "123 + 49", "1 - 9380"]) 13 | expected = " 11 3801 1 123 1\n+ 4 - 2999 + 2 + 49 - 9380\n---- ------ --- ----- ------" 14 | self.assertEqual(actual, expected, 'Expected different output when calling "arithmetic_arranger()" with ["11 + 4", "3801 - 2999", "1 + 2", "123 + 49", "1 - 9380"]') 15 | 16 | def test_too_many_problems(self): 17 | actual = arithmetic_arranger(["44 + 815", "909 - 2", "45 + 43", "123 + 49", "888 + 40", "653 + 87"]) 18 | expected = "Error: Too many problems." 19 | self.assertEqual(actual, expected, 'Expected calling "arithmetic_arranger()" with more than five problems to return "Error: Too many problems."') 20 | 21 | def test_incorrect_operator(self): 22 | actual = arithmetic_arranger(["3 / 855", "3801 - 2", "45 + 43", "123 + 49"]) 23 | expected = "Error: Operator must be '+' or '-'." 24 | self.assertEqual(actual, expected, '''Expected calling "arithmetic_arranger()" with a problem that uses the "/" operator to return "Error: Operator must be '+' or '-'."''') 25 | 26 | def test_too_many_digits(self): 27 | actual = arithmetic_arranger(["24 + 85215", "3801 - 2", "45 + 43", "123 + 49"]) 28 | expected = "Error: Numbers cannot be more than four digits." 29 | self.assertEqual(actual, expected, 'Expected calling "arithmetic_arranger()" with a problem that has a number over 4 digits long to return "Error: Numbers cannot be more than four digits."') 30 | 31 | def test_only_digits(self): 32 | actual = arithmetic_arranger(["98 + 3g5", "3801 - 2", "45 + 43", "123 + 49"]) 33 | expected = "Error: Numbers must only contain digits." 34 | self.assertEqual(actual, expected, 'Expected calling "arithmetic_arranger()" with a problem that contains a letter character in the number to return "Error: Numbers must only contain digits."') 35 | 36 | def test_solutions(self): 37 | actual = arithmetic_arranger(["32 - 698", "1 - 3801", "45 + 43", "123 + 49"], True) 38 | expected = " 32 1 45 123\n- 698 - 3801 + 43 + 49\n----- ------ ---- -----\n -666 -3800 88 172" 39 | self.assertEqual(actual, expected, 'Expected solutions to be correctly displayed in output when calling "arithmetic_arranger()" with arithmetic problems and a second argument of `True`.') 40 | 41 | 42 | if __name__ == "__main__": 43 | unittest.main() 44 | --------------------------------------------------------------------------------