From acf76306926f079ff146de5998620d0aaa2475f3 Mon Sep 17 00:00:00 2001 From: LTLA Date: Mon, 13 Nov 2023 15:55:37 -0800 Subject: [PATCH] Support conversion of NamedList to/from a regular dict. --- src/biocutils/NamedList.py | 30 +++++++++++++++++++++++++++--- tests/test_NamedList.py | 9 +++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/biocutils/NamedList.py b/src/biocutils/NamedList.py index bf4bb5a..40ee11e 100644 --- a/src/biocutils/NamedList.py +++ b/src/biocutils/NamedList.py @@ -1,4 +1,4 @@ -from typing import Sequence, Optional, Iterable, Union, Any +from typing import Sequence, Optional, Iterable, Union, Any, Dict from copy import deepcopy from .Names import Names @@ -15,16 +15,28 @@ class NamedList(list): a list, so it can be indexed as usual by integer positions or slices. """ - def __init__(self, iterable: Optional[Iterable] = None, names: Optional[Names] = None): + def __init__(self, iterable: Optional[Union[Iterable, Dict]] = None, names: Optional[Names] = None): """ Args: iterable: - Some iterable object. Alternatively None, for an empty list. + Some iterable object. + + Alternatively, a dictionary where the keys are strings. + + Alternatively None, for an empty list. names: List of names. This should have same length as ``iterable``. If None, defaults to an empty list. + + Ignored if ``iterable`` is a dictionary, in which case the + keys are used directly as the names. """ + if isinstance(iterable, dict): + original = iterable + iterable = original.values() + names = (str(y) for y in original.keys()) + if iterable is None: super().__init__() else: @@ -252,6 +264,18 @@ def __deepcopy__(self, memo=None, _nil=[]) -> "NamedList": """ return NamedList(deepcopy(self, memo, _nil), names=deepcopy(self_names, memo, _nil)) + def as_dict(self) -> dict[str, Any]: + """ + Returns: + A dictionary where the keys are the names and the values are the + list elements. Only the first occurrence of each name is returned. + """ + output = {} + for i, n in enumerate(self._names): + if n not in output: + output[n] = self[i] + return output + @subset_sequence.register def _subset_sequence_NamedList(x: NamedList, indices: Sequence[int]) -> NamedList: diff --git a/tests/test_NamedList.py b/tests/test_NamedList.py index 3c99860..e007ce7 100644 --- a/tests/test_NamedList.py +++ b/tests/test_NamedList.py @@ -39,6 +39,15 @@ def test_NamedList_basics(): assert z.get_names() == [ "a", "b", "c", "d" ] +def test_NamedList_dict(): + x = NamedList([1,2,3,4], names=['a', 'b', 'c', 'd']) + assert x.as_dict() == { "a": 1, "b": 2, "c": 3, "d": 4 } + + x = NamedList({ "c": 4, "d": 5, 23: 99 }) + assert x.get_names() == [ "c", "d", "23" ] + assert x == [ 4, 5, 99 ] + + def test_NamedList_setitem(): x = NamedList([1,2,3,4], names=["A", "B", "C", "D"]) x[0] = None