A library for list, dict, and UserDict manipulations.
Install arrayutilities via pip:
pip install arrayutilities
- Installation
- Usage
- accessible
- add
- add_prefixed_keys_to
- add_unprefixed_keys_to
- collapse
- dot
- exists
- filter_prefixed
- first
- flatten
- forget
- get
- has
- insert_after_key
- insert_before_key
- is_dict
- is_list
- join
- last
- list_to_dict
- list_to_string
- merge_recursive
- only
- prepend
- pull
- query
- random
- recursive_ksort
- set
- shape_filter
- shuffle
- sort_by_priority
- sort_recursive
- sort_recursive_desc
- stringify_keys
- strpos
- str_to_list
- undot
- usearch
- visit_recursive
- where
- where_not_none
- wrap
Here are descriptions and usage examples for each method in the Arr
class that you can get by:
from arrayutilities import Arr
@staticmethod
def accessible(value)
Determines if the given value is a list, dict, or UserDict.
Arguments:
value
Any - Value to determine if it is accessible.
Returns:
boolean
- Whether the the value is a list, dict, or UserDict.
# With dicts!
my_dict = { 'a': 1 }
Arr.accessible(my_dict) # Result: True
# With lists!
my_list = [ 1 ]
Arr.accessible(my_list) # Result: True
# Other things aren't accessible.
Arr.accessible('bacon') # Result: False
@staticmethod
def add(array, key, value)
Add an element to an array if it doesn't exist.
Arguments:
array
- Array to manipulate.key
- Key to use.value
- Value to inject.
Returns:
Manipulated array
# Add a key/value pair to a dict.
test_dict = {'a': 1}
result = Arr.add(test_dict, 'b', 2) # Result: {'a': 1, 'b': 2}
# Add a value to the end.
test_list = [1, 2, 3]
result = Arr.add(test_list, 4, 4) # Result: [1, 2, 3, 4]
@staticmethod
def add_prefixed_keys_to(array, recursive=False)
Duplicates any key not prefixed with '_' creating a prefixed duplicate one.
Arguments:
array
- Array to manipulate.recursive
bool, optional - Whether to recursively change the array. Defaults to False.
Returns:
Manipulated array.
# Prefix dicts.
my_dict = {'a': 1, 'b': 2}
result = Arr.add_prefixed_keys_to(my_dict) # Result: {'a': 1, 'b': 2, '_a': 1, '_b': 2}
# Prefix lists by converting to dicts.
my_list = [1, 2, 3]
result = Arr.add_prefixed_keys_to(my_list) # Result: {0: 1, 1: 2, 2: 3, '_0': 1, '_1': 2, '_2': 3}
@staticmethod
def add_unprefixed_keys_to(array, recursive=False)
Duplicates any key prefixed with '_' creating an un-prefixed duplicate one.
Arguments:
array
- Array to manipulate.recursive
bool, optional - Whether to recursively change the array. Defaults to False.
Returns:
Manipulated array.
# Unprefix dicts.
my_dict = {'_a': 1, '_b': 2}
result = Arr.add_unprefixed_keys_to(my_dict) # Result: {'_a': 1, '_b': 2, 'a': 1, 'b': 2}
# Recursively unprefix.
my_list = {'_a': {'_c': 3}, 'b': 2}
result = Arr.add_prefixed_keys_to(my_list) # Result: {'_a': {'_c': 3, 'c': 3}, 'b': 2, 'a': {'_c': 3, 'c': 3}}
@staticmethod
def collapse(array)
Collapse an array of arrays into a single array.
Arguments:
array
- Array of arrays to collapse.
Returns:
Collapsed array.
Arr.collapse([[1, 2, 3]]) # Result: [1, 2, 3]
Arr.collapse([[1, 2], [3, 4], [5]]) # Result: [1, 2, 3, 4, 5]
Arr.collapse([[1, 'a'], [3.5, 4], [True, None]]) # Result: [1, 'a', 3.5, 4, True, None]
Arr.collapse([[], [1, 2], [], [3, 4], []]) # Result: [1, 2, 3, 4]
@staticmethod
def dot(array, prepend='')
Flatten a multi-dimensional associative array with dots.
Arguments:
array
- Array to manipulate.prepend
str, optional - Value to prepend to dict keys. Defaults to ''.
Returns:
Manipulated array.
Arr.dot({'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}) # Result: {'a': 1, 'b.c': 2, 'b.d.e': 3}
Arr.dot({'a': 1}, 'init.') # Result: {'init.a': 1}
@staticmethod
def exists(array, key)
Determine if the given key exists in the provided array.
Arguments:
array
- Array to check.key
- Key to look for.
Returns:
boolean
- Whether or not the key exists.
Arr.exists({'a': 1, '1.5': 'yup'}, 'a') # Result: True
Arr.exists({'a': 1, 'b': 2}, 'c') # Result: False
@staticmethod
def filter_prefixed(array, prefix)
Filters an associative array non-recursively, keeping only the values attached to keys starting with the specified prefix.
Arguments:
array
- Array to filter.prefix
str|list - The prefix or prefixes of the keys to keep.
Returns:
Filtered array.
test_dict = {'pref_one': 1, 'pref_two': 2, 'nopref_three': 3}
Arr.filter_prefixed(test_dict, 'pref_') # Result: {'pref_one': 1, 'pref_two': 2}
test_dict = {'one': 1, 'two': 2, 'three': 3}
Arr.filter_prefixed(test_dict, 'pref_') # Result: {}
@staticmethod
def first(array, callback=None, default=None)
Return the first element in an array passing a given truth test.
Arguments:
array
- Array to look at.callback
Callable, optional - Callback function that returns a boolean of whether to match or not. Defaults to None.default
optional - Default value if no other value is found. Defaults to None.
Returns:
Found value or default.
Arr.first({'a': 1, 'b': 2, 'c': 3}) # Result: 1
# Find the first element that matches a callback.
test_dict = {'a': 1, 'b': 2, 'c': 3}
Arr.first(test_dict, callback=lambda v, k: k == 'b') # Result: 2
# Find the first element that matches a callback or return a default value.
test_dict = {'a': 1, 'b': 2, 'c': 3}
result = Arr.first(test_dict, callback=lambda v, k: k == 'z', default=None) # Result: None
@staticmethod
def flatten(array, depth=float('inf'))
Flatten a multi-dimensional array into a single level.
Arguments:
array
- Array to flatten.depth
number, optional - Number of nestings deep that should be flattened. Defaults to float('inf').
Returns:
Flattened array.
test_array = [1, [2, 3], 4]
Arr.flatten(test_array) # Result: [1, 2, 3, 4]
test_array = [1, [2, [3, [4, 5]]], 6]
Arr.flatten(test_array, depth=2) # Result: [1, 2, 3, [4, 5], 6]
@staticmethod
def forget(array, keys)
Remove one or many array items from a given array using "dot" notation.
Arguments:
array
- Array to manipulate.keys
str|array - Key or keys to remove.
test_dict = {'a': 1, 'b': 2, 'c': 3}
Arr.forget(test_dict, 'b') # Dict is now: {'a': 1, 'c': 3}
test_dict = {'a': 1, 'b': 2, 'c': {'d': 3, 'e': 4}}
Arr.forget(test_dict, ['a', 'c.d']) # Dict is now: {'b': 2, 'c': {'e': 4}}
@staticmethod
def get(array, keys, default=None)
Find a value inside of an array or object, including one nested a few levels deep.
Arguments:
array
- Array to searchkeys
str|array - Key or keys to get.default
optional - Value to return if none found. Defaults to None.
Returns:
_type_
- description
test_dict = {'a': 1, 'b': 2, 'c': 3}
Arr.get(test_dict, 'b') # Result: 2
test_dict = {'a': 1, 'b': {'c': { 'e': 2}, 'd': 3}}
Arr.get(test_dict, ['c', 'e']) # Result: 2
@staticmethod
def has(array, keys)
Check if an item or items exist in an array using "dot" notation.
Arguments:
array
- Array to check.keys
str|array - The indexes to search; in order the function will look from the first to the last.
Returns:
boolean
- Whether the key exists or not.
Arr.has({'a': 1, 'b': 2}, 'a') # Result: True
Arr.has({'a': 1, 'b': 2, 'c': 3}, ['a', 'b']) # Result: True
Arr.has({'a': 1, 'b': 2}, ['a', 'x']) # Result: False
@staticmethod
def insert_after_key(key, source_array, insert)
Insert an array after a specified key within another array.
Arguments:
key
str|number - The key of the array to insert after.source_array
array - The array to insert into.insert
Any - Value or array to insert.
Returns:
Manipulated array.
Arr.insert_after_key(2, [1, 2, 3, 4], 5) # Result: [1, 2, 3, 5, 4]
Arr.insert_after_key('b', {'a': 1, 'b': 2, 'c': 3}, {'new': 25}) # Result: {'a': 1, 'b': 2, 'new': 25, 'c': 3}
Arr.insert_after_key('b', {'a': 1, 'b': 2, 'c': 3}, 25) # Result: raises TypeError
@staticmethod
def insert_before_key(key, source_array, insert)
Insert an array before a specified key within another array.
Arguments:
key
str|number - The key of the array to insert before.source_array
array - The array to insert into.insert
Any - Value or array to insert.
Returns:
Manipulated array.
Arr.insert_before_key(1, [10, 20, 30], 15) # Result: [10, 15, 20, 30]
Arr.insert_before_key('b', {'a': 1, 'b': 2, 'c': 3}, {'new': 25}) # Result: {'a': 1, 'new': 25, 'b': 2, 'c': 3}
Arr.insert_before_key('b', {'a': 1, 'b': 2, 'c': 3}, 25) # Result: raises TypeError
@staticmethod
def is_dict(array)
Returns whether the array is a dict or not.
Arguments:
array
array - Array to check.
Returns:
boolean
- Whether the array is a dict or not.
Arr.is_dict({}) # Result: True
Arr.is_dict([]) # Result: False
Arr.is_dict(1) # Result: False
@staticmethod
def is_list(array)
Returns whether the array is a list or not.
Arguments:
array
array - Array to check.
Returns:
boolean
- Whether the array is a list or not.
Arr.is_list([]) # Result: True
Arr.is_list({}) # Result: False
Arr.is_list(1) # Result: False
@staticmethod
def join(array, glue, final_glue='')
Join all items using a string. The final items can use a separate glue string.
Arguments:
array
- Array to join.glue
str - String to inject between elements.final_glue
str, optional - String to inject between the final two elements. Defaults to ''.
Returns:
str
- Joined string.
Arr.join(['apple', 'banana', 'cherry'], ', ') # Result: 'apple, banana, cherry'
Arr.join(['apple', 'banana', 'cherry'], ', ', ', and ') # Result: 'apple, banana, and cherry'
@staticmethod
def last(array, callback=None, default=None)
Return the last element in an array passing a given truth test.
Arguments:
array
- Array to look at.callback
Callable, optional - Callback function that returns a boolean of whether to match or not. Defaults to None.default
optional - Default value if no other value is found. Defaults to None.
Returns:
Found value or default.
Arr.last([1, 2, 3]) # Result: 3
Arr.last([1, 2, 3, 4, 5], lambda x: x % 2 == 0) # Result: 4
Arr.last([1, 3, 5], lambda x: x % 2 == 0, default='no match') # Result: 'no match'
@staticmethod
def list_to_dict(value)
Converts a list to a dict.
Arguments:
value
list - A list to convert to a dict.
Returns:
Manipulated array.
Arr.list_to_dict([]) # Result: {}
Arr.list_to_dict(['apple', 'banana', 'cherry']) # Result: {0: 'apple', 1: 'banana', 2: 'cherry'}
Arr.list_to_dict('not a list') # Result: {0: 'not a list'}
@staticmethod
def list_to_string(list_items, sep=',')
Returns a list separated by the specified separator.
Arguments:
list_items
- Array of items.sep
str, optional - Separator. Defaults to ','.
Returns:
The list separated by the specified separator or the original list if the list is empty.
Arr.list_to_string(['apple', 'banana', 'cherry']) # Result: 'apple,banana,cherry'
Arr.list_to_string(['apple', 'banana', 'cherry'], sep=';') # Result: 'apple;banana;cherry'
@staticmethod
def merge_recursive(array1, array2)
Recursively merge two arrays preserving keys.
Arguments:
array1
- Starting array.array2
- Array to merge into the first.
Returns:
Merged array.
array1 = {'a': 1, 'b': 2}
array2 = {'b': 3, 'c': 4}
Arr.merge_recursive(array1, array2) # Result: {'a': 1, 'b': 3, 'c': 4}
array1 = {'a': {'b': 1, 'c': 2}}
array2 = {'a': {'c': 3, 'd': 4}}
Arr.merge_recursive(array1, array2) # Result: {'a': {'b': 1, 'c': 3, 'd': 4}}
@staticmethod
def only(array, keys)
Get a subset of the items from the given array.
Arguments:
array
- Array to search.keys
str|array - Key or keys to include in the final array.
Returns:
Array subset.
array = {'a': 1, 'b': 2, 'c': 3}
keys = ['a', 'c']
Arr.only(array, keys) # Result: {'a': 1, 'c': 3}
array = {'a': 1, 'b': 2, 'c': 3}
keys = ['x', 'y', 'z']
Arr.only(array, keys) # Result: {}
@staticmethod
def prepend(array, value, key=None)
Push an item onto the beginning of an array.
Arguments:
array
- Array to manipulate.value
Any - Value to prepend.key
string|number, optional - Key value for the prepended item. Defaults to None.
Returns:
Manipulated array.
Arr.prepend([2, 3, 4], 10) # Result: [10, 2, 3, 4]
Arr.prepend({'b': 2, 'c': 3}, 10) # Result: {0: 10, 'b': 2, 'c': 3}
Arr.prepend({'b': 2, 'c': 3}, 10, 'a') # Result: {'a': 10, 'b': 2, 'c': 3}
@staticmethod
def pull(array, key, default=None)
Get a value from the array, and remove it.
Arguments:
array
- Array to search and manipulate.key
str|number - Key to look for and fetch.default
Any, optional - Default value if none found. Defaults to None.
Returns:
Any
- The found value or default.
array = {'a': 1, 'b': 2, 'c': 3}
key = 'b'
Arr.pull(array, key) # Result: 2, Dictionary is now: {'a': 1, 'c': 3}
array = {'a': 1, 'b': 2, 'c': 3}
key = 'd'
default = 'not found'
result = Arr.pull(array, key, default) # Result: 'not found', Dictionary is now: {'a': 1, 'b': 2, 'c': 3}
@staticmethod
def query(data)
Convert the array into a query string.
Arguments:
data
- Array to convert.
Returns:
str
- URL query string.
data = {'name': 'John', 'age': 30, 'city': 'New York'}
Arr.query(data) # Result: 'name=John&age=30&city=New+York'
data = {'name': 'John', 'info': {'age': 30, 'city': 'New York'}}
Arr.query(data) # Result: 'name=John&info[age]=30&info[city]=New+York'
data = {'name': 'John', 'info': {'age': 30, 'city': ['New York', 'Phoenix']}}
Arr.query(data) # Result: 'name=John&info[age]=30&info[city][0]=New+York&info[city][1]=Phoenix'
@staticmethod
def random(array, number=None, preserve_keys=False)
Get one or a specified number of random values from an array.
Arguments:
array
- Array to search through.number
number, optional - Number of items to randomly grab. Defaults to None.preserve_keys
bool, optional - Whether the keys should be preserved or not. Defaults to False.
Raises:
ValueError
Returns:
Array with the random values.
Arr.random({'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}) # Result: 1 random value
Arr.random({'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}, number=3, preserve_keys=True) # Result: 3 random values with keys
@staticmethod
def recursive_ksort(array)
Recursively key-sort an array.
Arguments:
array
- The array to sort.
Returns:
Sorted array.
test_dict = {'b': 2, 'a': 1}
Arr.recursive_ksort(test_dict) # Result: {'a': 1, 'b': 2}
test_dict = {'b': 2, 'a': {'c': 3, 'b': 2}, 'd': [4, 3, 2], 'c': 'hello'}
Arr.recursive_ksort(test_dict) # Result: {'a': {'b': 2, 'c': 3}, 'b': 2, 'c': 'hello', 'd': [4, 3, 2]}
@staticmethod
def set(array, keys, value)
Set key/value within an array, can set a key nested inside of a multidimensional array.
Arguments:
array
- The array containing the key this sets.keys
array - To set a key nested multiple levels deep pass an array specifying each key in order as a value.Example
- array( 'lvl1', 'lvl2', 'lvl3' );value
Any - The value.
Returns:
The manipulated array.
Arr.set({}, 'a', 1) # Result: {'a': 1}
Arr.set({}, ['a', 'b', 'c'], 1) # Result: {'a': {'b': {'c': 1}}}
@staticmethod
def shape_filter(array, shape)
Shapes, filtering it, an array to the specified expected set of required keys.
Arguments:
array
- The input array to shape.shape
array - The shape to update the array with. It should only define keys or arrays of keys. Keys that have no values will be set to null. To add the key only if set, prefix the key with ?, e.g. ?foo.
Returns:
The manipulated array.
test_dict = {'a': 1, 'b': 2, 'c': 3}
shape = {'a': None, 'b': None}
Arr.shape_filter(test_dict, shape) # Result: {'a': 1, 'b': 2}
test_dict = {'a': {'b': 1, 'c': 2}, 'd': {'e': 3, 'f': 4}}
shape = {'a': {'b': None}}
Arr.shape_filter(test_dict, shape) # Result: {'a': {'b': 1}}
test_dict = {'a': 1, 'b': 2}
shape = {'a': None, 'c': None}
Arr.shape_filter(test_dict, shape) # Result: {'a': 1}
@staticmethod
def shuffle(array, seed=None)
Shuffle the given array and return the result.
Arguments:
array
- Array to shuffle.seed
Any, optional - The random number generator seed. Defaults to None.
Returns:
The shuffled array.
test_array = [i for i in range(1, 25)]
Arr.shuffle(test_array) # Result: Shuffled array
test_array = [i for i in range(1, 25)]
Arr.shuffle(test_array, 1234) # Result: Shuffled array with a specific seed
@staticmethod
def sort_by_priority(array)
Sort based on Priority.
Arguments:
array
- A dict with priority keys.
Returns:
Sorted dict.
input_array = [{'name': 'John', 'priority': 2}, {'name': 'Alice', 'priority': 1}, {'name': 'Bob', 'priority': 3}]
Arr.sort_by_priority(input_array) # Result: [{'name': 'Alice', 'priority': 1}, {'name': 'John', 'priority': 2}, {'name': 'Bob', 'priority': 3}]
@staticmethod
def sort_recursive(array, descending=False)
Recursively sort an array by keys and values.
Arguments:
array
- Array to sortdescending
bool, optional - Whether to sort in descending order or not. Defaults to False.
Returns:
Sorted array.
input_array = {'c': 3, 'a': {'b': 2, 'd': 4}}
Arr.sort_recursive(input_array) # Result: {'a': {'b': 2, 'd': 4}, 'c': 3}
input_array = {'c': 3, 'a': {'b': 2, 'd': 4}}
Arr.sort_recursive(input_array, descending=True) # Result: {'c': 3, 'a': {'d': 4, 'b': 2}}
@staticmethod
def sort_recursive_desc(array)
Recursively sort an array by keys and values in descending order.
Arguments:
array
- Array to sort
Returns:
Sorted array.
input_array = {'c': 3, 'a': {'b': 2, 'd': 4}}
Arr.sort_recursive_desc(input_array) # Result: {'c': 3, 'a': {'d': 4, 'b': 2}}
@staticmethod
def stringify_keys(input_array, prefix=None)
Stringifies the numeric keys of an array.
Arguments:
input_array
- Array to manipulate.prefix
str, optional - Prefix for array keys. Defaults to None.
Returns:
Manipulated array.
test_array = {'a': 1, 'b': 2, 'c': 3}
prefix = 'prefix_'
result = Arr.stringify_keys(test_array, prefix) # Result: {'prefix_a': 1, 'prefix_b': 2, 'prefix_c': 3}
test_array = {1: 'a', 2: 'b', 3: 'c'}
result = Arr.stringify_keys(test_array, 'sk_') # Result: {'sk_1': 'a', 'sk_2': 'b', 'sk_3': 'c'}
@staticmethod
def strpos(haystack, needles, offset=0)
Behaves exactly like the native PHP's strpos(), but accepts an array of needles.
Arguments:
haystack
str - String to search through.needles
str|array - Needle or needles to look for in the haystack.offset
int, optional - Starting position of search. Defaults to 0.
Returns:
_type_
- description
Arr.strpos("hello world", "world") # Result: 6
Arr.strpos("hello world", "earth") # Result: False
Arr.strpos("hello world", ["world", "ello"]) # Result: 1
# Offset of 6.
Arr.strpos("hello world hello", "hello", 6) # Result: 12
@staticmethod
def str_to_list(value, sep=',')
Converts a list to an array filtering out empty string elements.
Arguments:
value
str|number|None - A string representing a list of values separated by the specified separator or an array. If the list is a string (e.g. a CSV list) then it will urldecoded before processing.sep
str, optional - The char(s) separating the list elements; will be ignored if the list is an array. Defaults to ','.
Returns:
Manipulated array.
Arr.str_to_list("apple, banana, cherry") # Result: ["apple", "banana", "cherry"]
Arr.str_to_list("apple|banana|cherry", sep="|") # Result: ["apple", "banana", "cherry"]
Arr.str_to_list(" apple , banana , cherry ") # Result: ["apple", "banana", "cherry"]
Arr.str_to_list(" ") # Result: []
@staticmethod
def undot(obj)
Convert a flatten "dot" notation array into an expanded array.
Arguments:
obj
- Array to undot.
Returns:
Manipulated array.
Arr.undot({'a.b': 1, 'a.c.d': 2, 'e.f.g': 3}) # Result: {'a': {'b': 1, 'c': {'d': 2}}, 'e': {'f': {'g': 3}}}
# Duplicate keys. The last one takes precedence.
Arr.undot({'a.b': 1, 'a.b.c': 2, 'a.b.c.d': 3}) # Result: {'a': {'b': {'c': {'d': 3}}}}
@staticmethod
def usearch(needle, haystack, callback)
Searches an array using a callback and returns the index of the first match.
Arguments:
needle
Any - The element to search in the array.haystack
- The array to search.callback
function - A callback function with signature def x(needle, value, key) :bool that will be used to find the first match of needle in haystack.
Returns:
Either the index of the first match or False if no match was found.
def callback(needle, value, key):
return needle == value
haystack = {'a': 1, 'b': 2, 'c': 3}
needle = 2
Arr.usearch(needle, haystack, callback) # Result: 'b'
@staticmethod
def visit_recursive(input_array, visitor)
Recursively visits all elements of an array applying the specified callback to each element key and value.
Arguments:
input_array
- The input array whose nodes should be visited.visitor
- A callback function that will be called on each array item; the callback will receive the item key and value as input and should return an array that contains the update key and value in the shape [ <key>, <value> ]. Returning a null key will cause the element to be removed from the array.
Returns:
Manipulated array.
my_dict = {'a': 1, 'b': 2}
result = Arr.visit_recursive(my_dict, lambda k, v: (k, v * 2)) # Result: {'a': 2, 'b': 4}
@staticmethod
def where(array, callback)
Filter the array using the given callback.
Arguments:
array
- Array to filter.callback
function - Function that returns True if the element should be retained, False otherwise.
Returns:
The filtered array.
def callback(value, key):
return value % 2 == 0
array = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
Arr.where(array, callback) # Result: {'b': 2, 'd': 4}
@staticmethod
def where_not_none(array)
Filter items where the value is not None.
Arguments:
array
- Array to filter.
Returns:
The filtered array.
array = {'a': 1, 'b': None, 'c': 3, 'd': None}
Arr.where_not_none(array) # Result: {'a': 1, 'c': 3}
@staticmethod
def wrap(value)
If the given value is not a list, dict, or UserDict and not None, wrap it in one.
Arguments:
value
Any - Value to wrap.
Returns:
Wrapped value.
Arr.wrap(None) # Result: []
Arr.wrap(1) # Result: [1]
Arr.wrap('hello') # Result: ['hello']
Arr.wrap([1, 2, 3]) # Result: [1, 2, 3]