# -*- coding: utf-8 -*-
# This software and supporting documentation are distributed by
# Institut Federatif de Recherche 49
# CEA/NeuroSpin, Batiment 145,
# 91191 Gif-sur-Yvette cedex
# France
#
# This software is governed by the CeCILL-B license under
# French law and abiding by the rules of distribution of free software.
# You can use, modify and/or redistribute the software under the
# terms of the CeCILL-B license as circulated by CEA, CNRS
# and INRIA at the following URL "http://www.cecill.info".
#
# As a counterpart to the access to the source code and rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty and the software's author, the holder of the
# economic rights, and the successive licensors have only limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading, using, modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean that it is complicated to manipulate, and that also
# therefore means that it is reserved for developers and experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and, more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL-B license and that you accept its terms.
'''
Sorted dictionary behave like a dictionary but keep the item insertion
order.
* author: Yann Cointepas
* organization: NeuroSpin
* license: CeCILL B (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html)
In addition OrderedDict is provided here, either as the standard
collections.OrderedDict class if python version >= 2.7, or based on
SortedDictionary if python version < 2.7.
'''
from __future__ import print_function
from __future__ import absolute_import
__docformat__ = "restructuredtext en"
from collections import OrderedDict
import sys
import six
import inspect
from soma.undefined import Undefined
from six.moves import collections_abc
[docs]class SortedDictionary(dict):
'''
Sorted dictionary behave like a dictionary but keep the item insertion
order. In addition to python 2.7 OrderedDict, SortedDictionary also has
an :py:meth:`insert` method allowing insersion at a specified index
position.
Example:
::
from SortedDictionary import SortedDictionary
sd = SortedDictionary(('fisrt', 1), ('second', 2))
sd['third'] = 3
sd.insert(0, 'zero', 0)
sd.items() == [('zero', 0), ('fisrt', 1), ('second', 2), ('third', 3)]
'''
def __init__(self, *args):
'''
Initialize the dictionary with a list of (key, value) pairs.
'''
super(SortedDictionary, self).__init__()
self.sortedKeys = []
if len(args) == 1 and (
isinstance(args[0], list)
or inspect.isgenerator(args[0])
or (isinstance(args[0], collections_abc.ItemsView))):
elements = args[0] # dict / OrderedDict compatibility
else:
elements = args
for key, value in elements:
self[key] = value
[docs] def keys(self):
'''
Returns
-------
list
sorted list of keys
'''
return self.sortedKeys
[docs] def items(self):
'''
Returns
-------
list
sorted list of (key, value) pairs
'''
if six.PY2:
return list(self.iteritems())
else:
return self.iteritems()
[docs] def values(self):
'''
Returns
-------
values: list
sorted list of values
'''
if six.PY2:
return list(self.itervalues())
else:
return self.itervalues()
def __setitem__(self, key, value):
if key not in self:
if 'sortedKeys' not in self.__dict__:
# this happens during pickle.load() with python3
self.sortedKeys = []
self.sortedKeys.append(key)
super(SortedDictionary, self).__setitem__(key, value)
def __delitem__(self, key):
super(SortedDictionary, self).__delitem__(key)
self.sortedKeys.remove(key)
def __getstate__(self):
return list(self.items())
def __setstate__(self, state):
SortedDictionary.__init__(self, *state)
def __iter__(self):
'''
returns an iterator over the sorted keys
'''
return iter(self.sortedKeys)
[docs] def iterkeys(self):
'''
returns an iterator over the sorted keys
'''
return iter(self.sortedKeys)
[docs] def itervalues(self):
'''
returns an iterator over the sorted values
'''
for k in self:
yield self[k]
[docs] def iteritems(self):
'''
returns an iterator over the sorted (key, value) pairs
'''
for k in self:
try:
yield (k, self[k])
except KeyError:
print('!SortedDictionary error!', self.keys(), self.sortedKeys)
raise
[docs] def insert(self, index, key, value):
'''
insert a (key, value) pair in sorted dictionary before position
``index``. If ``key`` is already in the dictionary, a KeyError is
raised.
Parameters
----------
index: integer
index of key in the sorted keys
key: key to insert
value associated to key
'''
if key in self:
raise KeyError(key)
self.sortedKeys.insert(index, key)
super(SortedDictionary, self).__setitem__(key, value)
[docs] def index(self, key):
"""
Returns the index of the key in the sorted dictionary, or -1 if this key
isn't in the dictionary.
"""
try:
i = self.sortedKeys.index(key)
except Exception:
i = -1
return i
[docs] def clear(self):
'''
Remove all items from dictionary
'''
del self.sortedKeys[:]
super(SortedDictionary, self).clear()
[docs] def sort(self, key=None, reverse=False):
"""Sorts the dictionary using key function key.
Parameters
----------
key: function key
"""
self.sortedKeys.sort(key=key, reverse=reverse)
[docs] def compValues(self, key1, key2):
"""
Use this comparaison function in sort method parameter in order to sort
the dictionary by values.
if data[key1]<data[key2] return -1
if data[key1]>data[key2] return 1
if data[key1]==data[key2] return 0
"""
e1 = self[key1]
e2 = self[key2]
if e1 < e2:
return -1
elif e1 > e2:
return 1
return 0
[docs] def setdefault(self, key, value=None):
result = self.get(key, Undefined)
if result is Undefined:
self[key] = value
result = value
return result
[docs] def pop(self, key, default=Undefined):
if default is Undefined:
result = super(SortedDictionary, self).pop(key)
else:
result = super(SortedDictionary, self).pop(key, Undefined)
if result is Undefined:
return default
self.sortedKeys.remove(key)
return result
[docs] def popitem(self):
result = super(SortedDictionary, self).popitem()
try:
self.sortedKeys.remove(result[0])
except ValueError:
pass
return result
def __repr__(self):
return '{' + ', '.join(repr(k) + ': ' + repr(v)
for k, v in self.iteritems()) + '}'
[docs] def update(self, dict_obj):
for k, v in six.iteritems(dict_obj):
self[k] = v
[docs] def copy(self):
copied = self.__class__()
copied.update(self)
return copied