Notable Functions

The dict-plus library contains a multitude of interesting and useful functions. Here is some documentation on what they are and examples.

Squish

Combine two keys and their values into one new key, using a given function. Inverse of Expand

The function signature looks as follows:

func(value_list) -> new_value

This example squishes the keys ‘1’ and ‘asdf’ into a key called ‘tt’, with a function that takes the values, ‘8’ and ‘[E]’ respectively, and combines them.

d = DictPlus({"1": "8", "asdf": "[E]"})
d.squish(["1", "asdf"], "tt", lambda x: x[0] + x[1])
assert_eq(d, {"tt": "8[E]"})

Expand

Expand a single key into any number of keys. Inverse of Squish

The function signature looks as follows:

func(value) -> value_list

This example expands the key ‘tt’ into keys ‘1’ and ‘asdf’, with a function that takes the value of ‘tt’ and splits it into two strings. The output length must match the number of keys.

d = DictPlus({"tt": "8[E]"})
d.expand("tt", ["1", "asdf"], lambda x: (x[0], x[1:]))
assert_eq(d, {"1": "8", "asdf": "[E]"})

Map

Map a dictionary to new keys and values.

The function signature looks as follows:

func(key, value) -> new_key, new_value

In this example, a simple dictionary is mapped with a function that doubles it’s value and appends ‘a’ to it’s key. It is then un-done with an inverse function.

# Function element to element
def func_e2e(k, v):
    return str(k) + "a", v * 2

def invfunc_e2e(k, v):
    return str(k)[:-1], v / 2

d = DictPlus({"a": 1, "b": 2, "c": 3})

d.map(func_e2e)
assert_eq(d, {"aa": 2, "ba": 4, "ca": 6})

d.map(invfunc_e2e)
assert_eq(d, {"b": 2, "c": 3})

ReKey

Change the keys of a dictionary, keeping the values the same. Converse of Swap

The function signature looks as follows:

func(key) -> new_key

In this example, the keys in a dictionary are prepended with ‘a’, and then removed with an inverse function.

# Function key to key
def func_k2k(k):
    return k + "a"

def invfunc_k2k(k):
    return k[:-1]

d = DictPlus({"a": 1, "b": 2})
d.rekey(func_k2k)
assert_eq(d, {"aa": 1, "ba": 2})

d.rekey(invfunc_k2k)
assert_eq(d, {"a": 1, "b": 2})

Swap

Swap two keys, keeping the values the same. Converse of ReKey

In this example, the keys ‘a’ and ‘b’ are swapped

d = DictPlus({"a": 1, "b": 2})
d.swap("a", "b")
assert_eq(d["a"], 2)
assert_eq(d["b"], 1)

Add

Add two dictionaries with a function. Inverse of Sub

To ensure the dictionaries add the way you want it to, use an OrderedDictPlus If no function is given, it’s behavior is similar to update and can be used with addition symbol +

The function signature looks as follows:

func(key_value_pair1, key_value_pair2) -> new_key_value_pair

In this example, two dictionaries are added together using a function that adds their keys and values. Also, they are added without a function, and their keys are added, with common keys overriding, giving precedence to the right dictionary. Can be added with any dict-like object

# Function element,element to element
def func_ee2e(e1, e2):
    return e1.id + e2.id, e1.value + e2.value

d = OrderedDictPlus({"a": 1, "b": 2})
d2 = {"c": 3, "d": 4}

d3 = d + d2 # d3 equals {"a": 1, "b": 2, "c": 3, "d": 4}
d.add(d2) # d is now {"a": 1, "b": 2, "c": 3, "d": 4}

d = OrderedDictPlus({"a": 1, "b": 2}) # reset value of d
d.add(d2, func_ee2e) # d is now {"ac": 4, "bd": 6}

# This also works if you use '+'
# so d + {} -> result would be d
# When using the symbol, the original dictionary is not changed

Sub

Subtract two dictionaries with a function. Inverse of Add

To ensure the dictionaries subtract the way you want it to, use an OrderedDictPlus If no function is given, it’s behavior is similar to unupdate and can be used with subtraction symbol -

The function signature looks as follows:

func(key_value_pair1, key_value_pair2) -> new_key_value_pair

In this example, two dictionaries are subtracted using a function that subtracts their keys and values. Also, they are subtracted without a function, and their keys are removed. Unshared keys are ignored. Can be subtracted with any dict-like object

# Function element,element to element
def func_ee2e(e1, e2):
    return e1.id - e2.id, e1.value - e2.value

d = OrderedDictPlus({"a": 1, "b": 2, "c": 3, "d": 4})
d2 = {"c": 3, "d": 4}

d3 = d - d2 # d3 equals {"a": 1, "b": 2}
d.sub(d2) # d is now {"a": 1, "b": 2}

d = OrderedDictPlus({"a": 1, "b": 2, "c": 3, "d": 4}) # Reset value of d
d.sub(d2, func_ee2e) # d is now equals {"ac": 4, "bd": 6}

# This also works if you use '-'
# so d - {} -> result would be d
# When using the symbol, the original dictionary is not changed

Chop

Chops the dictionary into other dictionaries using a binning function Each keypair is assigned an integer value, and put in with other dictionaries with the same number. These ‘bins’ are then ordered relatively to each other and returned as a list.

Function signature looks as follows:

func(k, v) -> int

In this example, a dictionary with integer keys and string values is created, and chopped up into two other dictionaries where one has only even keys and the other only odd keys.

d = DictPlus({
    0: "a", 1: "b", 2: "c",
    3: "d", 4: "e", 5: "f",
    6: "g", 7: "h"
})

def func_chop(k, v):
    # Chops into even and odd keys
    return int(k % 2 != 0)

chopped = d.chop(func_chop)
# chopped[0] is now {0: "a", 2: "c", 4: "e", 6: "g"}
# chopped[1] is now {1: "b", 3: "d", 5: "f", 7: "h"}

Multiply

Multiply two dictionaries. Inverse of Divide

Multiply self with Iterable-like other using a function, such that every element of self is applied to every element of other

Function signature looks as follows:

func(element1, element2) -> (newkey1, newvalue1)

If the passed func is None, it defaults to:

func(element1, element2) -> ((key1, key2), (value1, value2)) and can be used with the multiplication symbol *

In the example below, a simple dictionary is multiplied in different ways with different functions and the results are shown in the comments

d = OrderedDictPlus({"a": 1, "b": 2, "c": 3})
d.multiply({})  # Multiplying by an empty dict is like multiplying by zero, so d is now {}
d = OrderedDictPlus(o)

d.multiply(o)
# d is now:
#{
#    ("a", "a"): (1, 1), ("a", "b"): (1, 2), ("a", "c"): (1, 3),
#    ("b", "a"): (2, 1), ("b", "b"): (2, 2), ("b", "c"): (2, 3),
#    ("c", "a"): (3, 1), ("c", "b"): (3, 2), ("c", "c"): (3, 3)
#}

d = OrderedDictPlus({"a": 1, "b": 2, "c": 3})  # Reset d
d.multiply({"a": 1, "b": 2, "c": 3}, lambda e1, e2: e1)  # Now we multiply using a function that always returns the first operand
# This is essentially multiplying by 1, we get the original dictionary

# Now we multiply with a more complex function, adds the keys and makes the values tuples
d.multiply({"a": 1, "b": 2, "c": 3}, lambda e1, e2: (e1.id + e2.id, (e1.value, e2.value)))
# d is now:
#{
#    "aa": (1, 1), "ab": (1, 2), "ac": (1, 3),
#    "ba": (2, 1), "bb": (2, 2), "bc": (2, 3),
#    "ca": (3, 1), "cb": (3, 2), "cc": (3, 3)
#}

# This also works if you use '*'
# so d * {}, result would be {}
# When using the symbol, the original dictionary is not changed

Divide

Divide two dictionaries. Inverse of Multiply

Divide self with Iterable-like other using a function.

Function signature looks as follows:

func(element1, element2) -> (newkey1, newvalue1)

If func_inv is None, it defaults to:

func_inv(element1, element2) -> (key1, value1) or (key2, value2)

and can be used with the division symbol /

Every element of self is applied to every element of other This is meant to undo what multiply did, it won’t work if you change the order of the dictionary.

In this example, the division is used to undo the result of a multiplication (o1), to retrieve the original dictionary (o2)

 o = {
        ("a", "a"): (1, 1), ("a", "b"): (1, 2), ("a", "c"): (1, 3),
        ("b", "a"): (2, 1), ("b", "b"): (2, 2), ("b", "c"): (2, 3),
        ("c", "a"): (3, 1), ("c", "b"): (3, 2), ("c", "c"): (3, 3)
    }
    o2 = {"a": 1, "b": 2, "c": 3}
    d = OrderedDictPlus(o)
    d.divide(o, lambda el, e2: (el.id[0], el.value[0]))
    # d is now o2

# This also works if you use '/'
# so d / {}, result would be {}
# When using the symbol, the original dictionary is not changed

FuncMap

Combine self and Iterable-like ‘other’ with a combine function and using a mapping function. Can be in-place or not.

Combine function signature looks as follows:

func(value1, value2) -> new_value

Mapping function signature looks as follows:

func(key) -> new_key

The following code combines the values by multiplying, and mapping to the opposite end of the dictionary

d = DictPlus({a: a for a in range(0, 100)})
dp = d.funcmap(
    {a: a for a in range(0, 100)},
    lambda _v1, _v2: _v1 * _v2,
    lambda _id: 99 - _id,
    inplace=False
)
# dp is now = {a: a * (99 - a) for a in range(0, 100)}

Fold Left

Reduce the Iterable using function ‘func’ and the ‘left’ grouping pattern Like (((1 + 2) + 3) + 4) so func(func(func(element1, element2), element3), element4)

The function signature looks as following: func(element1, element2) -> new_element

d = DictPlus({0: "a", 1: "b", 2: "c"})
r = d.fold_left(lambda e1, e2: (e1.id+e2.id,e1.value+e2.value))
# r is now {3: "abc"}

Fold Right

Reduce the Iterable using function ‘func’ and the ‘right’ grouping pattern Like (1 + (2 + (3 + 4))) so func(element1, func(element2, func(element3, element4)))

The function signature looks as following: func(element1, element2) -> new_element

d = DictPlus({0: "a", 1: "b", 2: "c"})
r = d.fold_left(lambda e1, e2: (e1.id+e2.id,e1.value+e2.value))
# r is now {3: "cba"}

Compare

Compare self with other using a comparison function, an aggregate function and a mapping function. If no mapping function is given, the default mapping function maps each element at index i in self to an element at index i in other. This can cause problems if other and self don’t have the same length

The compare function signature looks as following: func(element1, element2) -> new_value

The aggregate function signature looks as following: func([element1, element2, ..]) -> new_value

The mapping function signature looks as following: func(self_key) -> other_key

The following example combines the dictionary and simplifies it to find the truthy value of all the keys

d = DictPlus({False: "a", True: "b", False: "c"})
t = d.compare({False: "a", True: "b", False: "c"},
                comp=lambda el1,el2:el1.id,
                agg=dict_plus.funcs.Functions.AND,
                mapp=None)
# t is now false

LessThan

Less than is a Compare call with the function LT

GreaterThan

Greater than is a Compare call with the function GT

GreaterThanEqual

Greater tha equal is a Compare call with the function GTE

LessThanEqual

Greater than is a Compare call with the function LTE

Equal

Equal is a Compare call with the function EQ

Not Equal

Not Equal is a Compare call with the function NEQ