From e3dceb601c009ca65156e4f8801d695a32825e90 2020-06-11 14:46:06 From: Brett Smith Date: 2020-06-11 14:46:06 Subject: [PATCH] filters: Add iter_unique() function. --- diff --git a/conservancy_beancount/filters.py b/conservancy_beancount/filters.py index 94e082be8c7c19fb3aa600cc11f46fcf254e8372..ac28553ff95d657cabd2bddaa3357515fe4e78d0 100644 --- a/conservancy_beancount/filters.py +++ b/conservancy_beancount/filters.py @@ -24,9 +24,13 @@ from . import rtutil from typing import ( cast, + Hashable, Iterable, + Iterator, Optional, Pattern, + Set, + TypeVar, Union, ) from .beancount_types import ( @@ -37,6 +41,8 @@ from .beancount_types import ( Transaction, ) +# Saying Optional works around . +HashT = TypeVar('HashT', bound=Optional[Hashable]) Postings = Iterable[data.Posting] Regexp = Union[str, Pattern] @@ -72,6 +78,13 @@ def filter_for_rt_id(postings: Postings, ticket_id: Union[int, str]) -> Postings regexp = rtutil.RT.metadata_regexp(ticket_id, first_link_only=True) return filter_meta_match(postings, 'rt-id', regexp) +def iter_unique(seq: Iterable[HashT]) -> Iterator[HashT]: + seen: Set[HashT] = set() + for item in seq: + if item not in seen: + seen.add(item) + yield item + def remove_opening_balance_txn(entries: Entries) -> Optional[Transaction]: """Remove an opening balance transaction from entries returned by Beancount diff --git a/conservancy_beancount/reports/accrual.py b/conservancy_beancount/reports/accrual.py index 879f0d8613c79ee11b460c8dae521f4349b45278..32e57438ef58a7d9611a4192a93101a54c3f9925 100644 --- a/conservancy_beancount/reports/accrual.py +++ b/conservancy_beancount/reports/accrual.py @@ -207,17 +207,11 @@ class AccrualPostings(core.RelatedPostings): return item1 if all_same else self.INCONSISTENT def entities(self, pred: Callable[[data.Posting], bool]=bool) -> Iterator[MetaValue]: - seen: Set[MetaValue] = set() - for post in self: - if pred(post): - try: - entity = post.meta['entity'] - except KeyError: - pass - else: - if entity not in seen: - yield entity - seen.add(entity) + return filters.iter_unique( + post.meta['entity'] + for post in self + if pred(post) and 'entity' in post.meta + ) def first_links(self, key: MetaKey, default: Optional[str]=None) -> Iterator[Optional[str]]: return (post.meta.first_link(key, default) for post in self) diff --git a/tests/test_filters.py b/tests/test_filters.py index f709761d9a84043602a47005d662db9a0cb560e6..a6a5b03f4c0d88e23575016d183cc961eff7f24e 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -183,3 +183,6 @@ def test_audit_date(entry): assert actual is None else: assert actual == entry.date + +def test_iter_unique(): + assert list(filters.iter_unique('1213231')) == list('123')