././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1666817564.130471 indexed-1.3.0/0000755000175000017500000000000014326317034012522 5ustar00niklasniklas././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1587248374.0 indexed-1.3.0/LICENSE0000644000175000017500000000470013646676366013554 0ustar00niklasniklasPYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 3.8.2 software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 3.8.2 alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2020 Python Software Foundation; All Rights Reserved" are retained in Python 3.8.2 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python 3.8.2 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 3.8.2. 4. PSF is making Python 3.8.2 available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 3.8.2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.8.2 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.8.2, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python 3.8.2, Licensee agrees to be bound by the terms and conditions of this License Agreement. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1587248374.0 indexed-1.3.0/MANIFEST.in0000644000175000017500000000004313646676366014301 0ustar00niklasniklasinclude LICENSE include README.rst ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1666817564.130471 indexed-1.3.0/PKG-INFO0000644000175000017500000001026114326317034013617 0ustar00niklasniklasMetadata-Version: 2.1 Name: indexed Version: 1.3.0 Summary: A dictionary that is indexed by insertion order. Home-page: http://github.com/niklasf/indexed.py Author: Niklas Fiekas Author-email: niklas.fiekas@backscattering.de License: PSFL Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Description-Content-Type: text/x-rst License-File: LICENSE indexed.IndexedOrderedDict: a dictionary that is indexed by insertion order =========================================================================== .. image:: https://github.com/niklasf/indexed.py/actions/workflows/test.yml/badge.svg :target: https://github.com/niklasf/indexed.py/actions/workflows/test.yml :alt: Test .. image:: https://badge.fury.io/py/indexed.svg :target: https://pypi.python.org/pypi/indexed :alt: PyPI package Introduction ------------ ``indexed.IndexedOrderedDict`` (alias ``indexed.Dict``) is feature compatible with ``collections.OrderedDict`` as of Python 3.11 and can be used as a drop in replacement. The main difference is that key, value and item views support accessing elements by their index. .. code-block:: python d = indexed.IndexedOrderedDict() d["first-key"] = "first-value" d["second-key"] = "second-value" d["third-key"] = "third-value" values = d.values() assert values[2] == "third-value" assert d.keys().index("second-key") == 1 Features -------- * Access keys, values and items by index, e.g., ``d.keys()[5]``. * Find the index of a key, e.g., ``d.keys().index("key")``. * Sort keys in place, e.g., ``d.sort()``. Excluding those additions the API is the same as the API of ``collections.OrderedDict()``. Installing ---------- :: pip install indexed Performance ----------- Performance is practically on the same order of magnitude as the built in ``collections.OrderedDict``, with exceptions in bold: ================= ========== ================== ======== ====================== d ``collections.OrderedDict`` ``indexed.IndexedOrderedDict`` ----------------- ----------------------------- ------------------------------- Operation Avergage Worst case Average Worst case ================= ========== ================== ======== ====================== d.copy() O(n) O(n) O(n) O(n) ----------------- ---------- ------------------ -------- ---------------------- d[key] O(1) O(n) O(1) O(n) ----------------- ---------- ------------------ -------- ---------------------- d[key] = value O(1) O(n) [#a]_ O(1) O(n) [#a]_ ----------------- ---------- ------------------ -------- ---------------------- del d[key] **O(1)** O(n) O(n) O(n) ----------------- ---------- ------------------ -------- ---------------------- d.keys()[i] O(n) [#k]_ O(n) [#k]_ **O(1)** **O(1)** ----------------- ---------- ------------------ -------- ---------------------- d.values()[i] O(n) [#v]_ O(n) [#v]_ **O(1)** O(n) ----------------- ---------- ------------------ -------- ---------------------- d.items()[i] O(n) [#v]_ O(n) [#v]_ **O(1)** O(n) ----------------- ---------- ------------------ -------- ---------------------- d.keys().index(x) O(n) [#v]_ O(n) [#v]_ O(n) O(n) ================= ========== ================== ======== ====================== .. [#a] These are amortized_ worst case runtimes. .. [#k] This does not work in Python 3 because ``colections.KeysView`` is not indexable. One of the theoretically best work arounds is ``next(itertools.islice(d.keys(), i, i + 1))``. .. [#v] Assuming the theoretically best possible workaround. License ------- This library is derived from CPython's ``collections.OrderedDict`` and licensed under the PSFL. See the LICENSE file for the full license text. .. _amortized: http://en.wikipedia.org/wiki/Amortized_analysis ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666817292.0 indexed-1.3.0/README.rst0000644000175000017500000000700514326316414014214 0ustar00niklasniklasindexed.IndexedOrderedDict: a dictionary that is indexed by insertion order =========================================================================== .. image:: https://github.com/niklasf/indexed.py/actions/workflows/test.yml/badge.svg :target: https://github.com/niklasf/indexed.py/actions/workflows/test.yml :alt: Test .. image:: https://badge.fury.io/py/indexed.svg :target: https://pypi.python.org/pypi/indexed :alt: PyPI package Introduction ------------ ``indexed.IndexedOrderedDict`` (alias ``indexed.Dict``) is feature compatible with ``collections.OrderedDict`` as of Python 3.11 and can be used as a drop in replacement. The main difference is that key, value and item views support accessing elements by their index. .. code-block:: python d = indexed.IndexedOrderedDict() d["first-key"] = "first-value" d["second-key"] = "second-value" d["third-key"] = "third-value" values = d.values() assert values[2] == "third-value" assert d.keys().index("second-key") == 1 Features -------- * Access keys, values and items by index, e.g., ``d.keys()[5]``. * Find the index of a key, e.g., ``d.keys().index("key")``. * Sort keys in place, e.g., ``d.sort()``. Excluding those additions the API is the same as the API of ``collections.OrderedDict()``. Installing ---------- :: pip install indexed Performance ----------- Performance is practically on the same order of magnitude as the built in ``collections.OrderedDict``, with exceptions in bold: ================= ========== ================== ======== ====================== d ``collections.OrderedDict`` ``indexed.IndexedOrderedDict`` ----------------- ----------------------------- ------------------------------- Operation Avergage Worst case Average Worst case ================= ========== ================== ======== ====================== d.copy() O(n) O(n) O(n) O(n) ----------------- ---------- ------------------ -------- ---------------------- d[key] O(1) O(n) O(1) O(n) ----------------- ---------- ------------------ -------- ---------------------- d[key] = value O(1) O(n) [#a]_ O(1) O(n) [#a]_ ----------------- ---------- ------------------ -------- ---------------------- del d[key] **O(1)** O(n) O(n) O(n) ----------------- ---------- ------------------ -------- ---------------------- d.keys()[i] O(n) [#k]_ O(n) [#k]_ **O(1)** **O(1)** ----------------- ---------- ------------------ -------- ---------------------- d.values()[i] O(n) [#v]_ O(n) [#v]_ **O(1)** O(n) ----------------- ---------- ------------------ -------- ---------------------- d.items()[i] O(n) [#v]_ O(n) [#v]_ **O(1)** O(n) ----------------- ---------- ------------------ -------- ---------------------- d.keys().index(x) O(n) [#v]_ O(n) [#v]_ O(n) O(n) ================= ========== ================== ======== ====================== .. [#a] These are amortized_ worst case runtimes. .. [#k] This does not work in Python 3 because ``colections.KeysView`` is not indexable. One of the theoretically best work arounds is ``next(itertools.islice(d.keys(), i, i + 1))``. .. [#v] Assuming the theoretically best possible workaround. License ------- This library is derived from CPython's ``collections.OrderedDict`` and licensed under the PSFL. See the LICENSE file for the full license text. .. _amortized: http://en.wikipedia.org/wiki/Amortized_analysis ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1666817564.130471 indexed-1.3.0/indexed/0000755000175000017500000000000014326317034014142 5ustar00niklasniklas././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666817499.0 indexed-1.3.0/indexed/__init__.py0000644000175000017500000001322314326316733016261 0ustar00niklasniklas"""Provides a dictionary that is indexed by insertion order.""" __author__ = "Niklas Fiekas" __email__ = "niklas.fiekas@backscattering.de" __version__ = "1.3.0" __license__ = "PSFL" import collections import collections.abc import operator import reprlib class IndexedOrderedDict(dict): """A dictionary that is indexed by insertion order.""" def __init__(self, *args, **kwds): """ Initialize an ordered dictionary. The signature is the same as regular dictionaries. Keyword argument order is preserved. """ if len(args) > 1: raise TypeError('expected at most 1 arguments, got %d' % len(args)) self._map = [] self.__update(*args, **kwds) def __setitem__(self, key, value, *, __dict_setitem=dict.__setitem__): """iod.__setitem__(i, y) <==> iod[i] = y""" if key not in self: self._map.append(key) __dict_setitem(self, key, value) def __delitem__(self, key, *, __dict_delitem=dict.__delitem__): """iod.__delitem__(y) <==> del iod[y]""" __dict_delitem(self, key) self._map.remove(key) def __iter__(self): """iod.__iter__() <==> iter(iod)""" return self._map.__iter__() def __reversed__(self): """iod.__reversed__() <==> reversed(iod)""" return self._map.__reversed__() def clear(self): """iod.clear() -> None. Remove all items from iod.""" self._map.clear() dict.clear(self) def popitem(self, last=True): """ iod.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned LIFO order if last is true or FIFI order if false. """ key = self._map.pop() if last else self._map.pop(0) value = dict.pop(self, key) return key, value def move_to_end(self, key, last=True): """ Move an existing element to the end (or beginning if last==False). Raises KeyError if the element does not exist. When last=True, acts like a faster version of self[key]=self.pop(key). """ self._map.remove(key) if last: self._map.append(key) else: self._map.insert(0, key) update = __update = collections.abc.MutableMapping.update __ne__ = collections.abc.MutableMapping.__ne__ def keys(self): return IndexedKeysView(self) def values(self): return IndexedValuesView(self) def items(self): return IndexedItemsView(self) __marker = object() def pop(self, key, default=__marker): """ iod.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised. """ if key in self: result = self[key] del self[key] return result if default is self.__marker: raise KeyError(key) return default def setdefault(self, key, default=None): """ iod.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in d """ if key in self: return self[key] self[key] = default return default def sort(self, *, key=None, reverse=False): """Sort the dictionary by key in place.""" self._map.sort(key=key, reverse=reverse) @reprlib.recursive_repr() def __repr__(self): """iod.__repr__() <==> repr(iod)""" if not self: return '%s()' % (self.__class__.__name__, ) return '%s(%r)' % (self.__class__.__name__, list(self.items())) def __reduce__(self): """Return state information for pickling""" inst_dict = vars(self).copy() for k in vars(IndexedOrderedDict()): inst_dict.pop(k, None) return self.__class__, (), inst_dict or None, None, iter(self.items()) def copy(self): """od.copy() -> a shallow copy of iod""" return self.__class__(self) @classmethod def fromkeys(cls, iterable, value=None): """ IOD.fromkeys(S[,v]) -> New indexed ordered dictionary with keys from S. If not specified, the value defaults to None. """ self = cls() for key in iterable: self[key] = value return self def __eq__(self, other): """ iod.__eq__(y) <==> iod==y. Comparison to another IOD is order-sensitive while comparison to a regular mapping is order-insensitive. """ if isinstance(other, collections.OrderedDict) or isinstance(other, IndexedOrderedDict): return dict.__eq__(self, other) and all(map(operator.eq, self, other)) return dict.__eq__(self, other) def __or__(self, other): if not isinstance(other, dict): return NotImplemented new = self.__class__(self) new.update(other) return new def __ror__(self, other): if not isinstance(other, dict): return NotImplemented new = self.__class__(other) new.update(self) return new def __ior__(self, other): self.update(other) return self Dict = IndexedOrderedDict class IndexedKeysView(collections.abc.KeysView): def __getitem__(self, index): return self._mapping._map[index] def index(self, x): return self._mapping._map.index(x) class IndexedValuesView(collections.abc.ValuesView): def __getitem__(self, index): key = self._mapping._map[index] return self._mapping[key] class IndexedItemsView(collections.abc.ItemsView): def __getitem__(self, index): key = self._mapping._map[index] return key, self._mapping[key] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1666817564.130471 indexed-1.3.0/indexed.egg-info/0000755000175000017500000000000014326317034015634 5ustar00niklasniklas././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666817564.0 indexed-1.3.0/indexed.egg-info/PKG-INFO0000644000175000017500000001026114326317034016731 0ustar00niklasniklasMetadata-Version: 2.1 Name: indexed Version: 1.3.0 Summary: A dictionary that is indexed by insertion order. Home-page: http://github.com/niklasf/indexed.py Author: Niklas Fiekas Author-email: niklas.fiekas@backscattering.de License: PSFL Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Description-Content-Type: text/x-rst License-File: LICENSE indexed.IndexedOrderedDict: a dictionary that is indexed by insertion order =========================================================================== .. image:: https://github.com/niklasf/indexed.py/actions/workflows/test.yml/badge.svg :target: https://github.com/niklasf/indexed.py/actions/workflows/test.yml :alt: Test .. image:: https://badge.fury.io/py/indexed.svg :target: https://pypi.python.org/pypi/indexed :alt: PyPI package Introduction ------------ ``indexed.IndexedOrderedDict`` (alias ``indexed.Dict``) is feature compatible with ``collections.OrderedDict`` as of Python 3.11 and can be used as a drop in replacement. The main difference is that key, value and item views support accessing elements by their index. .. code-block:: python d = indexed.IndexedOrderedDict() d["first-key"] = "first-value" d["second-key"] = "second-value" d["third-key"] = "third-value" values = d.values() assert values[2] == "third-value" assert d.keys().index("second-key") == 1 Features -------- * Access keys, values and items by index, e.g., ``d.keys()[5]``. * Find the index of a key, e.g., ``d.keys().index("key")``. * Sort keys in place, e.g., ``d.sort()``. Excluding those additions the API is the same as the API of ``collections.OrderedDict()``. Installing ---------- :: pip install indexed Performance ----------- Performance is practically on the same order of magnitude as the built in ``collections.OrderedDict``, with exceptions in bold: ================= ========== ================== ======== ====================== d ``collections.OrderedDict`` ``indexed.IndexedOrderedDict`` ----------------- ----------------------------- ------------------------------- Operation Avergage Worst case Average Worst case ================= ========== ================== ======== ====================== d.copy() O(n) O(n) O(n) O(n) ----------------- ---------- ------------------ -------- ---------------------- d[key] O(1) O(n) O(1) O(n) ----------------- ---------- ------------------ -------- ---------------------- d[key] = value O(1) O(n) [#a]_ O(1) O(n) [#a]_ ----------------- ---------- ------------------ -------- ---------------------- del d[key] **O(1)** O(n) O(n) O(n) ----------------- ---------- ------------------ -------- ---------------------- d.keys()[i] O(n) [#k]_ O(n) [#k]_ **O(1)** **O(1)** ----------------- ---------- ------------------ -------- ---------------------- d.values()[i] O(n) [#v]_ O(n) [#v]_ **O(1)** O(n) ----------------- ---------- ------------------ -------- ---------------------- d.items()[i] O(n) [#v]_ O(n) [#v]_ **O(1)** O(n) ----------------- ---------- ------------------ -------- ---------------------- d.keys().index(x) O(n) [#v]_ O(n) [#v]_ O(n) O(n) ================= ========== ================== ======== ====================== .. [#a] These are amortized_ worst case runtimes. .. [#k] This does not work in Python 3 because ``colections.KeysView`` is not indexable. One of the theoretically best work arounds is ``next(itertools.islice(d.keys(), i, i + 1))``. .. [#v] Assuming the theoretically best possible workaround. License ------- This library is derived from CPython's ``collections.OrderedDict`` and licensed under the PSFL. See the LICENSE file for the full license text. .. _amortized: http://en.wikipedia.org/wiki/Amortized_analysis ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666817564.0 indexed-1.3.0/indexed.egg-info/SOURCES.txt0000644000175000017500000000026714326317034017525 0ustar00niklasniklasLICENSE MANIFEST.in README.rst setup.py indexed/__init__.py indexed.egg-info/PKG-INFO indexed.egg-info/SOURCES.txt indexed.egg-info/dependency_links.txt indexed.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666817564.0 indexed-1.3.0/indexed.egg-info/dependency_links.txt0000644000175000017500000000000114326317034021702 0ustar00niklasniklas ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666817564.0 indexed-1.3.0/indexed.egg-info/top_level.txt0000644000175000017500000000001014326317034020355 0ustar00niklasniklasindexed ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1666817564.130471 indexed-1.3.0/setup.cfg0000644000175000017500000000004614326317034014343 0ustar00niklasniklas[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666817491.0 indexed-1.3.0/setup.py0000755000175000017500000000177114326316723014251 0ustar00niklasniklas#!/usr/bin/env python import os import setuptools def read(filename): with open(os.path.join(os.path.dirname(__file__), filename)) as f: return f.read() setuptools.setup( name="indexed", version="1.3.0", author="Niklas Fiekas", author_email="niklas.fiekas@backscattering.de", description="A dictionary that is indexed by insertion order.", long_description=read("README.rst"), long_description_content_type="text/x-rst", license="PSFL", url="http://github.com/niklasf/indexed.py", packages=["indexed"], test_suite="test", python_required=">=3.7", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Python Software Foundation License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules", ], )