#!/usr/bin/python
# -*- coding: utf-8 -*-
""" Like mygenerators, there specific often needed utilities for lists. I really like the ``as_list`` decorator for
an empty args generator. Also, take a look at DictList.
"""
from __future__ import print_function, division
from collections import MutableSequence, Mapping, Sequence
from copy import deepcopy, copy
import operator as op
import mygenerators
import wrapt
__author__ = 'Stephan Sahm <Stephan.Sahm@gmx.de>'
[docs]def deflatten(flat_li, *original_li):
"""
rebuilds elements of flat_li to match list structure of original_li (or tuple if given as *args)
Parameters
----------
flat_li
original_li
Returns
-------
flat_li with nesting like original_li
"""
if len(original_li) == 1:
original_li = original_li[0]
deflatten_li = []
i = 0
for el in original_li:
if isinstance(el, Sequence):
deflatten_li.append(flat_li[i:i+len(el)])
i += len(el)
else:
deflatten_li.append(flat_li[i])
i += 1
return deflatten_li
@wrapt.decorator
[docs]def return_list(wrapped, instance, args, kwargs):
return list(wrapped(*args, **kwargs))
[docs]def as_list(gen):
""" generator decorator which executes the generator and returns results as list"""
return list(gen())
[docs]def sequencefy(o):
return o if isinstance(o, Sequence) else [o]
[docs]def findall(l, o):
""" find all indices from list ``l`` where entries match specific object ``o``
:param l: list to search in
:param o: object to search for
:return: list of indices where l[i] == o
"""
return [i for i, u in enumerate(l) if u==o]
[docs]def getall(l, idx):
""" get all entries of list ``l`` at positions ``idx``
:param l: list
:param idx: indices
:return: respective sublist
"""
return [l[i] for i in idx]
[docs]def remove(l, key=None):
if key is None:
raise ValueError("currently key needs to be given")
i = 0
while i < len(l):
if key(l[i]):
del l[i]
else:
i += 1
[docs]def remove_duplicates(l):
""" removes duplicates in place by using del call """
unique = set() # we use a set because ``elem in set`` is much faster than ``elem in list``
i = 0
while i < len(l):
elem = l[i]
if elem in unique:
del l[i]
else:
unique.add(elem)
i += 1
return l
[docs]def add_up(iterable):
return reduce(op.add, iterable)
[docs]def deepflatten(maybe_iterable):
return list(mygenerators.deepflatten(maybe_iterable))
[docs]def shallowflatten(maybe_iterable):
return list(mygenerators.shallowflatten(maybe_iterable))
[docs]class DictList(MutableSequence):
"""
firstly, this is a list
secondly, this is a dict pointing to lists of elements in DictList (kept unique, i.e. automatically removing duplicates)
"""
def __init__(self, *list_entries, **names_to_list_of_list_entries):
self.list = list_entries
self.dict = names_to_list_of_list_entries
# update all dictionary keys, keeping only references pointing to the list
for k in self.dict:
self.dict[k] = [v for v in self.dict[k] if v in self.list]
def __copy__(self):
return DictList(*self.list, **self.dict)
# merged list-dict interface
# --------------------------
def __getitem__(self, item):
if isinstance(item, basestring):
return self.dict[item]
else:
return self.list[item]
def __setitem__(self, key, new):
""" key types supported: string, int
maintains references to values in the list """
if isinstance(key, basestring):
self.dict[key] = [v for v in new if v in self.list]
elif isinstance(key, int):
old = self.list[key]
self.list[key] = new
# update all dictionary keys:
for k in self.dict:
self.dict[k] = [new if v is old else v for v in self.dict[k]]
else:
raise NotImplementedError("only string and int are allowed as keys for setting (e.g. no slices!)")
def __delitem__(self, key):
""" key types supported: string, int, slice """
if isinstance(key, basestring):
# this makes most sense if value refers to elements in the list
del self.dict[key]
else:
del self.list[key]
# update all dictionary keys:
for k in self.dict:
self.dict[k] = [v for v in self.dict[k] if v in self.list]
# could be made more efficient by looking at the concrete key-index, but this way it is much simpler to read =)
# list-like interface
# -------------------
def __iter__(self):
return iter(self.list)
def __len__(self):
return len(self.list)
# Operators
# ---------
def __add__(self, other):
cp = copy(self)
cp += other
return cp
def __iadd__(self, other):
if isinstance(other, MutableSequence):
self.list += other
elif isinstance(other, DictList):
self.list += other.list
for k in other.dict:
if k in self.dict:
self.dict[k] += other.dict[k]
remove_duplicates(self.dict[k])
else:
self.dict[k] = other.dict[k]
else:
raise NotImplemented
return self
def __radd__(self, other):
if isinstance(other, MutableSequence):
return DictList(*(other + self.list), **self.dict)
raise NotImplemented
# Proxy Mapping interface of self.dict (methods copied from collections.Mapping)
# ------------------------------------
[docs] def get(self, key, default=None):
'D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.'
return self.dict.get(key, default)
[docs] def iterkeys(self):
'D.iterkeys() -> an iterator over the keys of D'
return self.dict.iterkeys()
[docs] def itervalues(self):
'D.itervalues() -> an iterator over the values of D'
return self.dict.itervalues()
[docs] def iteritems(self):
'D.iteritems() -> an iterator over the (key, value) items of D'
return self.dict.iteritems()
[docs] def keys(self):
"D.keys() -> list of D's keys"
return self.dict.keys()
[docs] def items(self):
"D.items() -> list of D's (key, value) pairs, as 2-tuples"
return self.dict.items()
[docs] def values(self):
"D.values() -> list of D's values"
return self.dict.values()
[docs] def insert(self, index, value):
self.list.insert(index, value)