Skip to main content

Ordered Multivalue Dictionary

Project description

Better OrderedMultiDict

License Hits PyPI version PyPI - Python Versions

Overview

Better OrderedMultiDict provides OrderedMultiDict, a fast*, ordered multivalued dictionary.

OrderedMultiDict is multivalued, which means that there can be multiple items with the same key:

from better_orderedmultidict import OrderedMultiDict
dictionary = OrderedMultiDict()
dictionary.add("umfahren", "to drive around")
dictionary.add("umfahren", "to drive over, to knock sth. over")
print(dictionary.getall("umfahren"))  # ["to drive around", "to drive over, to knock sth. over"]

OrderedMultiDict retains the insertion order of all keys and values:

from better_orderedmultidict import OrderedMultiDict
dictionary = OrderedMultiDict()
dictionary.add('a', 'A1')
dictionary.add('b', 'B')
dictionary.add('a', 'A2')
print(', '.join(dictionary.values()))      # A1, B, A2
print(', '.join(dictionary.unique_keys())) # a, b

dictionary.popfirstitem()
print(', '.join(dictionary.values()))      # B, A2
print(', '.join(dictionary.unique_keys())) # b, a

Better OrderedMultiDict requires Python 3.12+ and is fully type annotated.

Performance Comparison

Creating / iterating over dictionary with 500'000 entries with all keys being different:

OrderedMultiDict [omdict][omdict_LINK] speedup [bolton][bolton_LINK]
OrderedMultiDict
speedup
create 164.3 ms 548.0 ms 3.2x 304.1 ms 1.9x
addall / addlist 82.3 ms 311.7 ms 3.8x 139.6 ms 1.7x
update / updateall1) 262.2 ms 531.5 ms 2.0x 325.5 ms 1.2x
extend / update_extend 156.4 ms -- -- 285.5 ms 1.8x
copy 100.1 ms 646.7 ms 6.5x 350.7 ms 3.4x
iterate over items 11.2 ms 97.0 ms 8.6x 81.0 ms 7.2x
iterate over values 12.3 ms 77.3 ms 6.3x 81.3 ms 6.3x
iterate over keys 12.6 ms 78.6 ms 6.2x 53.0 ms 4.2x
iterate over unique keys 42.4 ms 23.7 ms slower 94.1 ms 2.2x
pop last item until empty 268.7 ms 501.3 ms 1.9x 884.3 ms 3.2x

Creating / iterating over dictionary with 500'000 entries, but only 100 unique keys distributed randomly:

OrderedMultiDict [omdict][omdict_LINK] speedup [bolton][bolton_LINK]
OrderedMultiDict
speedup
create 89.7 ms 484.5 ms 5.4x 258.8 ms 2.7x
addall / addlist 84.7 ms 317.1 ms 3.7x 137.5 ms 1.6x
update / updateall1) 110.8 ms 491.8 ms 4.4x 278.2 ms 2.5x
extend / update_extend 91.3 ms -- -- 257.3 ms 2.7x
copy 24.0 ms 595.8 ms 24.8x 307.8 ms 12.4x
iterate over items 11.2 ms 102.5 ms 9.2x 77.4 ms 6.8x
iterate over values 17.0 ms 81.0 ms 4.8x 80.1 ms 4.8x
iterate over keys 16.9 ms 78.3 ms 4.6x 50.5 ms 3.0x
iterate over unique keys 18.8 ms < 0.1 ms slower 36.0 ms 1.9x
pop last item until empty 235.7 ms 484.8 ms 2.1x 401.8 ms 1.7x

1): omdict. updateall() has slightly different behavior: omdict keeps the positions for already existing keys, but OrderedMultiDict and bolton's OrderedMultiDict do not:

from better_orderedmultidict import OrderedMultiDict
from orderedmultidict import omdict
omd1 = OrderedMultiDict([(1,1), (2,2), (1,11), (2, 22), (3,3)])
omd2 = omdict([(1,1), (2,2), (1,11), (2, 22), (3,3)])
omd1.update([(1, '1'), (3, '3'), (1, '11')])
omd2.updateall([(1, '1'), (3, '3'), (1, '11')])
print(list(omd1.items()))         # prints [(2, 2), (2, 22), (1, '1'), (3, '3'), (1, '11')]
print(list(omd2.iterallitems()))  # prints [(1, '1'), (2, 2), (1, '11'), (2, 22), (3, '3')]

Examples

OrderedMultiDict can have multiple values per key:

from better_orderedmultidict import OrderedMultiDict
omd = OrderedMultiDict()
omd[1] = 1
print(omd[1])   # prints: 1

omd.add(1, 11)
print(omd[1])           # prints: 11
print(omd.get(1))       # prints: 11
print(omd.getlast(1))   # prints: 11
print(omd.getfirst(1))  # prints: 1
print(omd.getall(1))    # prints: [1, 11]

# adding multiple values at once:
omd.addall(2, [2, 22])
print(omd.getall(2))    # prints: [2, 22]

# __setitem__ overrides all existing entries for the given key:
omd[1] = 111
print(list(omd.values()))  # prints: [2, 22, 111]

OrderedMultiDict retains insertion order:

from better_orderedmultidict import OrderedMultiDict
omd = OrderedMultiDict()
omd.addall(1, [1, 11])
omd.add(2, 2)
omd.add(3, 3)
omd.add(2, 22)
omd.add(3, 33)
omd.add(1, 111)

print(omd.getall(1))    # prints: [1, 11, 111]
print(list(omd.values()))  # prints: [1, 11, 2, 3, 22, 33, 111]

del omd[2]
print(list(omd.values()))  # prints: [1, 11, 3, 33, 111]
omd.popfirst(3)
print(list(omd.values()))  # prints: [1, 11, 33, 111]
omd.poplast(1)
print(list(omd.values()))  # prints: [1, 11, 33]
omd.poplast(1)
print(list(omd.values()))  # prints: [1, 33]

Method parity with dict is only somewhat retained

from better_orderedmultidict import OrderedMultiDict
d, omd = dict(), OrderedMultiDict()
d.update([(1,1), (1,11), (2,2), (2,22)])
omd.update([(1,1), (1,11), (2,2), (2,22)])

print(d[1], omd[1])  # prints: (11, 11)
d[3] = omd[3] = 3
print(d[3], omd[3])  # prints: (3, 3)

d[3] = omd[3] = 33
print(d[3], omd[3])  # prints: (33, 33)

# But there are differences:
print(len(d), len(omd))  # prints: (3, 5)
d.pop(2)
omd.pop(2)
print(d.get(2), omd.get(2))  # prints: (None, 2)

Iterating over unique keys

from better_orderedmultidict import OrderedMultiDict
omd = OrderedMultiDict([(1,1), (2,2), (1,11), (2,22)])

print(len(omd.keys()))  # prints: 4
print(len(omd.unique_keys()))  # prints: 2

for key in reversed(omd.unique_keys()):
    print(f"{key}: {omd.getall(key)}")
# prints:
# 1: [1, 11]
# 2: [2, 22]

Installation

Installation is simple:

$ pip install better_orderedmultidict

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

better_orderedmultidict-0.2.1.tar.gz (14.7 kB view details)

Uploaded Source

Built Distribution

better_orderedmultidict-0.2.1-py3-none-any.whl (10.4 kB view details)

Uploaded Python 3

File details

Details for the file better_orderedmultidict-0.2.1.tar.gz.

File metadata

File hashes

Hashes for better_orderedmultidict-0.2.1.tar.gz
Algorithm Hash digest
SHA256 e08c75b513c5d8d14a25cf7705f84b276b33c1a2ef269bc0da54ce8a42d53cb6
MD5 f765628eeb5a5be98ac0d197a4b206ad
BLAKE2b-256 5ff2223982b517b210e23d66200c6d456d37ddf67dd87e3f2bd86696cd15743a

See more details on using hashes here.

File details

Details for the file better_orderedmultidict-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for better_orderedmultidict-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 18d017f1ba4bd08e9de49a8c9d858de3185ef989f98433d10e477bf0a532507e
MD5 36b830fc77e12ac17240647b4a8fcf68
BLAKE2b-256 97fd7105ae752125cea976a7b16164777d17b9ef2ae16b0cb56dd6b97a338ca3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page