Skip to content

Commit

Permalink
Merge pull request #2 from josex2r/feature/array-class
Browse files Browse the repository at this point in the history
Feature/array class
  • Loading branch information
josex2r authored May 18, 2022
2 parents 58b911f + c8f9291 commit 922635d
Show file tree
Hide file tree
Showing 16 changed files with 413 additions and 65 deletions.
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# python_js

`python_js` is a simple Python module which ports some JS functions to Python.

This is not production safe and it's made for learning purposes. I know this is not Pythonic's stylish but it's just a playground to learn some basics about this language.

## Features

- Some array methods
- Type checking
- Linter
- Local publishing using `Pypi` test environment.
- Automatic publish using `Github Actions` + `semantic-release`
- Local & CI testing using `tox`

## Requirements

- `python >= 3.10`
- `pipenv`

## Install

```bash
pipenv install
```

## Running linters

```bash
pipenv run lint
```

## Running tests

Run all tests using `tox`:

```bash
tox
```

Using the current python version:

```bash
pipenv run test
```

## Publish

Test `Pypi` from local:

```bash
pipenv run publish_raw
```
6 changes: 5 additions & 1 deletion python_js/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@
This is not production safe and it's made for learning purposes.
"""

__author__ = 'Jose Luis Represa'
from .array import Array

__all__ = [
"Array",
]
23 changes: 23 additions & 0 deletions python_js/array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# from .array.map import map, CallbackType, ReturnValue
from __future__ import annotations

from typing import List, TypeVar, Callable, Any

from .array_methods import concat, filter, map, reduce

T = TypeVar("T")
K = TypeVar("K")


class Array(List[T]):
def concat(self, *args: List[Any]) -> Array[Any]:
return Array(concat(self, *args))

def filter(self, func: Callable[[T, int], bool]) -> Array[Any]:
return Array(filter(self, func))

def map(self, func: Callable[[T, int], K]) -> Array[K]:
return Array(map(self, func))

def reduce(self, func: Callable[[Any, T, int], Any], initialValue: K | None = None) -> Any:
return reduce(self, func, initialValue=initialValue)
7 changes: 0 additions & 7 deletions python_js/array/__init__.py

This file was deleted.

49 changes: 0 additions & 49 deletions python_js/array/map.py

This file was deleted.

13 changes: 13 additions & 0 deletions python_js/array_methods/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from .concat import concat
from .filter import filter
from .map import map
from .reduce import reduce

# __all__ = tuple(k for k in locals() if not k.startswith("_"))

__all__ = [
"concat",
"filter",
"map",
"reduce"
]
30 changes: 30 additions & 0 deletions python_js/array_methods/concat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from __future__ import annotations

from typing import Any, List


def concat(*args: List[Any]) -> List[Any]:
"""
Merge two or more arrays.
This method does not change the existing arrays, but instead returns a new array.
Args:
*args: The iterables to mergle.
Return:
A new element.
Examples
--------
concat(
["a", "b", "c"],
[1, 2, 3]
) # ["a", "b", "c", 1, 2, 3]
"""
result: List[Any] = []

for x in args:
result = [*result, *x]

return result
25 changes: 25 additions & 0 deletions python_js/array_methods/filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from __future__ import annotations
from typing import Iterable, TypeVar, List, Callable, Any

T = TypeVar('T')


def filter(iterable: Iterable[T], func: Callable[[T, int], bool]) -> List[Any]:
"""
Return a new array with the the elements which passes the callback.
Args:
iterable: The iterable to map.
func: The callback which filters the values.
Return:
A filtered List.
Examples
--------
filter([1, 2, 3], lambda x: x > 2) # [3]
"""
return [
value for index, value in enumerate(iterable) if func(value, index)
]
24 changes: 24 additions & 0 deletions python_js/array_methods/map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from __future__ import annotations
from typing import Iterable, TypeVar, List, Callable

T = TypeVar('T')
K = TypeVar('K')


def map(iterable: Iterable[T], func: Callable[[T, int], K]) -> List[K]:
"""
Return a new array with the results of calling func on each element.
Args:
iterable: The iterable to map.
func: The callback which transforms each value.
Return:
A List with each value result.
Examples
--------
map([1, 2, 3], lambda x: x * 2) # [2, 4, 6]
"""
return [func(value, index) for index, value in enumerate(iterable)]
36 changes: 36 additions & 0 deletions python_js/array_methods/reduce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations
from typing import Iterable, TypeVar, Callable, Any

T = TypeVar('T')
K = TypeVar('K')


def reduce(iterable: Iterable[T], func: Callable[[Any, T, int], Any], initialValue: K | None = None) -> Any:
"""
Executes a user-supplied “reducer” callback function on each element of
the array, in order, passing in the return value from the calculation on
the preceding element. The final result of running the reducer across all
elements of the array is a single value.
Args:
iterable: The iterable to map.
func: The callback which transforms all the values.
Return:
A new element.
Examples
--------
reduce(
["a", "b", "c"],
lambda acc, x, i: (acc[x] = i) and acc,
{}
) # { 'a': 0, 'b': 1, 'c': 2 }
"""
result = initialValue

for i, x in enumerate(iterable):
result = func(result, x, i)

return result
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[semantic_release]
version_variable = setup.py:__version__

[flake8]
max-line-length = 120
23 changes: 23 additions & 0 deletions tests/array/test_concat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from typing import List

from python_js.array_methods.concat import concat

seasons: List[str] = ['Winter', 'Summer', 'Fall', 'Spring']
numbers: List[int] = [1, 2]
seasons_and_numbers: List[int | str] = [*seasons, *numbers]


def test_returned_value_instance() -> None:
formatted_seasons: List[str | int] = concat(seasons, numbers)

# Then
assert isinstance(formatted_seasons, List)


def test_input_argument_iterable() -> None:
result: List[str | int] = concat(seasons, numbers)

# Then
assert result == seasons_and_numbers
29 changes: 29 additions & 0 deletions tests/array/test_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from __future__ import annotations

from typing import Callable, List

from python_js.array_methods.filter import filter

seasons: List[str] = ['Winter', 'Summer', 'Fall', 'Spring']
numbers: List[int] = [1, 2]
seasons_and_numbers: List[int | str] = [*seasons, *numbers]


def test_returned_value_instance() -> None:
formatted_seasons: List[str] = filter(seasons, lambda x, i: bool(x))

# Then
assert isinstance(formatted_seasons, List)


def test_input_argument_iterable() -> None:
format_str: Callable[[str | int, int],
bool] = lambda value, index: isinstance(value, str)

formatted_seasons_and_numbers: List[str] = filter(
seasons_and_numbers, format_str)

# Then
assert formatted_seasons_and_numbers == [
'Winter', 'Summer', 'Fall', 'Spring'
]
25 changes: 17 additions & 8 deletions tests/array/test_map.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
from __future__ import annotations

from typing import Callable, List

from python_js.array import map
from python_js.array_methods.map import map

seasons: List[str] = ['Winter', 'Summer', 'Fall', 'Spring']
numbers: List[int] = [1, 2, 3, 4]
seasond_and_numbers: List[int | str] = [*seasons, *numbers]
numbers: List[int] = [1, 2]
seasons_and_numbers: List[int | str] = [*seasons, *numbers]


def test_returned_value_instance() -> None:
formatted_seasons: List[str] = map(seasons, lambda x, i: x)

# Then
assert isinstance(formatted_seasons, List)


def test_map():
format_str: Callable[[str, int],
def test_input_argument_iterable() -> None:
format_str: Callable[[str | int, int],
str] = lambda value, index: f"{value} ({index})"

formatted_seasons = map(seasons, format_str)
formatted_seasons_and_numbers: List[str] = map(
seasons_and_numbers, format_str)

# Then
assert formatted_seasons == [
'Winter (0)', 'Summer (1)', 'Fall (2)', 'Spring (3)'
assert formatted_seasons_and_numbers == [
'Winter (0)', 'Summer (1)', 'Fall (2)', 'Spring (3)', '1 (4)', '2 (5)'
]
Loading

0 comments on commit 922635d

Please sign in to comment.