Skip to content

Commit

Permalink
feat: includes(elem)
Browse files Browse the repository at this point in the history
  • Loading branch information
eturino committed Jul 31, 2021
1 parent 771b422 commit 48d2d66
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 19 deletions.
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Python port of [KeySet in TypeScript](https://github.com/eturino/ts-key-set) and

TBD

## TODO

- remove
- union

## Limitations

- for now, only KeySet of strings
Expand All @@ -22,22 +27,22 @@ Enum that represents the 4 types of KeySets:

Methods exposed:

### `key_set_type`
### `key_set_type()`

returns the `KeySetType` enum

### `elements`
### `elements()`

returns the set with the elements. It will be blank for `All` and `None`.

### `represents_xxx` methods
### `represents_xxx()` methods

- `represents_all`: returns True if the KeySet is ALL
- `represents_none`: returns True if the KeySet is NONE
- `represents_some`: returns True if the KeySet is SOME
- `represents_all_except_some`: returns True if the KeySet is ALL_EXCEPT_SOME

### `invert`
### `invert()`

Returns a new KeySet that represents the inverse Set of this one.

Expand All @@ -47,3 +52,7 @@ Returns a new KeySet that represents the inverse Set of this one.
### `intersect(other)`

Returns a new KeySet with the intersection (A ∩ B) of both Sets.

### `includes(element)`

Returns a new KeySet with the intersection (A ∩ B) of both Sets.
41 changes: 31 additions & 10 deletions key_set/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def invert(self) -> KeySet:
"""
pass

@abstractmethod
def includes(self, _elem: str) -> bool:
"""Returns True if the set represented by this includes the elem."""
pass

@abstractmethod
def clone(self) -> KeySet:
"""Returns a new KeySet that represents the same Set of this one."""
Expand Down Expand Up @@ -86,6 +91,10 @@ def clone(self) -> KeySetAll:
"""Returns a new KeySet that represents the same Set of this one."""
return KeySetAll()

def includes(self, _elem: str) -> bool:
"""Returns True if the set represented by this includes the elem."""
return True

def intersect(self, other: KeySet) -> KeySet:
"""Returns a new KeySet that represents the intersection (A ∩ B)."""
return other.clone()
Expand Down Expand Up @@ -122,6 +131,10 @@ def clone(self) -> KeySetNone:
"""Returns a new KeySet that represents the same Set of this one."""
return KeySetNone()

def includes(self, _elem: str) -> bool:
"""Returns True if the set represented by this includes the elem."""
return False

def intersect(self, _other: KeySet) -> KeySetNone:
"""Returns a new KeySet that represents the intersection (A ∩ B)."""
return self.clone()
Expand Down Expand Up @@ -165,18 +178,22 @@ def clone(self) -> KeySetSome:
"""Returns a new KeySet that represents the same Set of this one."""
return KeySetSome(self.elements())

def includes(self, elem: str) -> bool:
"""Returns True if the set represented by this includes the elem."""
return elem in self._elements

def intersect(self, other: KeySet) -> KeySet:
"""Returns a new KeySet that represents the intersection (A ∩ B)."""
if other.represents_all():
return self.clone()
if other.represents_none():
return other.clone()
if other.represents_some():
els = self._elements.intersection(other.elements())
return build_some(els)
elems = self._elements.intersection(other.elements())
return build_some_or_none(elems)
if other.represents_all_except_some():
els = self._elements.difference(other.elements())
return build_some(els)
elems = self._elements.difference(other.elements())
return build_some_or_none(elems)
return NotImplemented


Expand Down Expand Up @@ -221,34 +238,38 @@ def clone(self) -> KeySetAllExceptSome:
"""Returns a new KeySet that represents the same Set of this one."""
return KeySetAllExceptSome(self.elements())

def includes(self, elem: str) -> bool:
"""Returns True if the set represented by this includes the elem."""
return elem not in self._elements

def intersect(self, other: KeySet) -> KeySet:
"""Returns a new KeySet that represents the intersection (A ∩ B)."""
if other.represents_all():
return self.clone()
if other.represents_none():
return other.clone()
if other.represents_some():
els = other.elements().difference(self._elements)
return build_some(els)
elems = other.elements().difference(self._elements)
return build_some_or_none(elems)
if other.represents_all_except_some():
els = self._elements.union(other.elements())
return build_all_except_some(els)
elems = self._elements.union(other.elements())
return build_all_except_some_or_all(elems)
return NotImplemented


TS = Union[KeySetSome, KeySetNone]
TAES = Union[KeySetAllExceptSome, KeySetAll]


def build_some(seq: TKS) -> TS:
def build_some_or_none(seq: TKS) -> TS:
"""Returns NONE if seq is blank, or SOME otherwise."""
if len(seq) > 0:
return KeySetSome(seq)
else:
return KeySetNone()


def build_all_except_some(seq: TKS) -> TAES:
def build_all_except_some_or_all(seq: TKS) -> TAES:
"""Returns ALL if seq is blank, or ALL_EXCEPT_SOME otherwise."""
if len(seq) > 0:
return KeySetAllExceptSome(seq)
Expand Down
4 changes: 4 additions & 0 deletions tests/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ def test_intersect_all_except_some(self) -> None:
assert actual.elements() == {'a', 'b'}
assert actual == other
assert actual is not other

def test_includes(self) -> None:
ks = KeySetAll()
assert ks.includes('a')
8 changes: 8 additions & 0 deletions tests/test_all_except_some.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,11 @@ def test_intersect_all_except_some_without_common_keys(self) -> None:
actual = ks.intersect(other)
assert actual.represents_all_except_some()
assert actual.elements() == {'a', 'b', 'c', 'd'}

def test_includes_included(self) -> None:
ks = KeySetAllExceptSome({'a', 'b'})
assert not ks.includes('a')

def test_includes_missing(self) -> None:
ks = KeySetAllExceptSome({'a', 'b'})
assert ks.includes('c')
10 changes: 5 additions & 5 deletions tests/test_builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@
from typing import List

import key_set # noqa: F401
from key_set.base import build_all_except_some, build_some
from key_set.base import build_all_except_some_or_all, build_some_or_none


class TestBuilds: # noqa: D101

def test_build_some_with_blank(self) -> None:
keys: List[str] = []
actual = build_some(keys)
actual = build_some_or_none(keys)
assert actual.represents_none()

def test_build_some_with_elements(self) -> None:
actual = build_some(['A'])
actual = build_some_or_none(['A'])
assert actual.represents_some()
assert actual.elements() == {'A'}

def test_build_all_except_some_with_blank(self) -> None:
keys: List[str] = []
actual = build_all_except_some(keys)
actual = build_all_except_some_or_all(keys)
assert actual.represents_all()

def test_build_all_except_some_with_elements(self) -> None:
actual = build_all_except_some(['A'])
actual = build_all_except_some_or_all(['A'])
assert actual.represents_all_except_some()
assert actual.elements() == {'A'}
4 changes: 4 additions & 0 deletions tests/test_none.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ def test_intersect_all_except_some(self) -> None:
assert actual.represents_none()
assert actual == ks
assert actual is not ks

def test_includes(self) -> None:
ks = KeySetNone()
assert not ks.includes('a')
8 changes: 8 additions & 0 deletions tests/test_some.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,11 @@ def test_intersect_all_except_some_without_common_keys(self) -> None:
actual = ks.intersect(other)
assert actual.represents_some()
assert actual.elements() == {'a', 'b'}

def test_includes_included(self) -> None:
ks = KeySetSome({'a', 'b'})
assert ks.includes('a')

def test_includes_missing(self) -> None:
ks = KeySetSome({'a', 'b'})
assert not ks.includes('c')

0 comments on commit 48d2d66

Please sign in to comment.