Module sverchok.utils.dictionary
Expand source code
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
import numpy as np
from itertools import zip_longest
class SvDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
"""
Special attribute for keeping meta data which helps to unwrap dictionaries properly for `dictionary out` node
This attribute should be set only by nodes which create new dictionaries or change existing one
Order of keys in `self.inputs` dictionary should be the same as order of input data of the dictionary
`self.inputs` dictionary should keep data in next format:
{data.id: # it helps to track data, if is changed `dictionary out` recreate new socket for this data
{'type': any socket type with which input data is related,
'name': name of output socket,
'nest': only for 'SvDictionarySocket' type, should keep dictionary with the same data structure
}}
For example, there is the dictionary:
dict('My values': [0,1,2], 'My vertices': [(0,0,0), (1,0,0), (0,1,0)])
Metadata should look in this way:
self.inputs = {'Values id':
{'type': 'SvStringsSocket',
{'name': 'My values',
{'nest': None
}
'Vertices id':
{'type': 'SvVerticesSocket',
{'name': 'My vertices',
{'nest': None
}
}
"""
self.inputs = dict()
def copy(self):
result = SvDict(super().copy())
result.inputs = self.inputs
return result
@staticmethod
def from_dict(d):
if isinstance(d, SvDict):
return d.copy()
sv_dict = SvDict(d)
inputs = {}
for key, value in d.items():
if isinstance(value, SvDict):
nested = value.inputs
sock_type = 'SvDictionarySocket'
elif isinstance(value, dict):
nested = SvDict.from_dict(value).inputs
sock_type = 'SvDictionarySocket'
else:
nested = None
sock_type = 'SvStringsSocket'
inputs[key] = {
'type': sock_type,
'name': key,
'nest': nested
}
sv_dict.inputs = inputs
return sv_dict
@staticmethod
def get_inputs(d):
if isinstance(d, SvDict) and d.inputs:
return d.inputs
else:
inputs = {}
for key in d:
inputs[key] = {
'type': 'SvStringsSocket',
'name': key,
'nest': None
}
return inputs
def get_max_nesting_level(self):
max_level = 0
for key, value in self.items():
if isinstance(value, SvDict):
level = value.get_max_nesting_level() + 1
elif isinstance(value, dict):
level = SvDict(value).get_max_nesting_level() + 1
else:
level = 0
if level > max_level:
max_level = level
return max_level
def get_nested_keys_at(self, level):
if level == 0:
return set(self.keys())
else:
keys = set()
for value in self.values():
if isinstance(value, SvDict):
v_keys = value.get_nested_keys_at(level-1)
elif isinstance(value, dict):
v_keys = SvDict(value).get_nested_keys_at(level-1)
else:
v_keys = set()
keys.update(v_keys)
return keys
def get_nested_inputs_at(self, level):
if level == 0:
return SvDict.get_inputs(self)
else:
inputs = dict()
for value in self.values():
if isinstance(value, SvDict):
v_inputs = value.get_nested_inputs_at(level-1)
elif isinstance(value, dict):
v_inputs = SvDict(value).get_nested_inputs_at(level-1)
else:
v_inputs = None
if v_inputs:
inputs.update(v_inputs)
return inputs
def get_all_nested_keys(self):
max_level = self.get_max_nesting_level()
all_keys = []
for level in range(max_level):
keys = self.get_nested_keys_at(level)
all_keys.append(keys)
return all_keys
def get_sock_types(self, level=0):
if level == 0:
inputs = SvDict.get_inputs(self)
return [input['type'] for input in inputs.values()]
else:
types = None
for value in self.values():
if isinstance(value, SvDict):
v_types = value.get_sock_types(level-1)
elif isinstance(value, dict):
v_types = SvDict(value).get_sock_types(level-1)
else:
v_types = None
if v_types:
if types is None:
types = v_types
else:
for i, (old_type, new_type) in enumerate(zip_longest(types, v_types)):
if old_type is None:
types[i] = new_type
elif old_type != new_type:
types[i] = 'SvStringsSocket'
return types
class SvApproxDict(object):
def __init__(self, pairs=None, precision=6):
self.precision = precision
self.keys = np.array([])
self.values = np.array([])
if pairs is not None:
for key, value in pairs:
self[key] = value
def tolerance(self):
return 10**(-self.precision)
def __repr__(self):
items = [f"{key}: {value}" for key, value in zip(self.keys, self.values)]
s = ", ".join(items)
return "{" + s + "}"
def __setitem__(self, key, value):
if len(self.keys) == 0:
self.keys = np.array([key])
self.values = np.array([value])
return
i = self.keys.searchsorted(key)
if i > 0:
smaller = self.keys[i-1]
else:
smaller = None
if i < len(self.keys):
greater = self.keys[i]
else:
greater = None
if smaller is not None and (key - smaller) < self.tolerance():
#self.keys[i-1] = 0.5*(key + self.keys[i-1])
self.values[i-1] = value
return
if greater is not None and (greater - key) < self.tolerance():
#self.keys[i] = 0.5*(key + self.keys[i])
self.values[i] = value
return
self.keys = np.insert(self.keys, i, key)
self.values = np.insert(self.values, i, value)
def get(self, key, default=None):
if len(self.keys) == 0:
return default
i = self.keys.searchsorted(key)
if i > 0:
smaller = self.keys[i-1]
if (key - smaller) < self.tolerance():
return self.values[i-1]
if i < len(self.keys):
greater = self.keys[i]
if (greater - key) < self.tolerance():
return self.values[i]
return default
def __getitem__(self, key):
value = self.get(key, None)
if value is None:
raise KeyError("Key not found")
return value
def items(self):
return zip(self.keys, self.values)
Classes
class SvApproxDict (pairs=None, precision=6)
-
Expand source code
class SvApproxDict(object): def __init__(self, pairs=None, precision=6): self.precision = precision self.keys = np.array([]) self.values = np.array([]) if pairs is not None: for key, value in pairs: self[key] = value def tolerance(self): return 10**(-self.precision) def __repr__(self): items = [f"{key}: {value}" for key, value in zip(self.keys, self.values)] s = ", ".join(items) return "{" + s + "}" def __setitem__(self, key, value): if len(self.keys) == 0: self.keys = np.array([key]) self.values = np.array([value]) return i = self.keys.searchsorted(key) if i > 0: smaller = self.keys[i-1] else: smaller = None if i < len(self.keys): greater = self.keys[i] else: greater = None if smaller is not None and (key - smaller) < self.tolerance(): #self.keys[i-1] = 0.5*(key + self.keys[i-1]) self.values[i-1] = value return if greater is not None and (greater - key) < self.tolerance(): #self.keys[i] = 0.5*(key + self.keys[i]) self.values[i] = value return self.keys = np.insert(self.keys, i, key) self.values = np.insert(self.values, i, value) def get(self, key, default=None): if len(self.keys) == 0: return default i = self.keys.searchsorted(key) if i > 0: smaller = self.keys[i-1] if (key - smaller) < self.tolerance(): return self.values[i-1] if i < len(self.keys): greater = self.keys[i] if (greater - key) < self.tolerance(): return self.values[i] return default def __getitem__(self, key): value = self.get(key, None) if value is None: raise KeyError("Key not found") return value def items(self): return zip(self.keys, self.values)
Methods
def get(self, key, default=None)
-
Expand source code
def get(self, key, default=None): if len(self.keys) == 0: return default i = self.keys.searchsorted(key) if i > 0: smaller = self.keys[i-1] if (key - smaller) < self.tolerance(): return self.values[i-1] if i < len(self.keys): greater = self.keys[i] if (greater - key) < self.tolerance(): return self.values[i] return default
def items(self)
-
Expand source code
def items(self): return zip(self.keys, self.values)
def tolerance(self)
-
Expand source code
def tolerance(self): return 10**(-self.precision)
class SvDict (*args, **kwargs)
-
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
Expand source code
class SvDict(dict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) """ Special attribute for keeping meta data which helps to unwrap dictionaries properly for `dictionary out` node This attribute should be set only by nodes which create new dictionaries or change existing one Order of keys in `self.inputs` dictionary should be the same as order of input data of the dictionary `self.inputs` dictionary should keep data in next format: {data.id: # it helps to track data, if is changed `dictionary out` recreate new socket for this data {'type': any socket type with which input data is related, 'name': name of output socket, 'nest': only for 'SvDictionarySocket' type, should keep dictionary with the same data structure }} For example, there is the dictionary: dict('My values': [0,1,2], 'My vertices': [(0,0,0), (1,0,0), (0,1,0)]) Metadata should look in this way: self.inputs = {'Values id': {'type': 'SvStringsSocket', {'name': 'My values', {'nest': None } 'Vertices id': {'type': 'SvVerticesSocket', {'name': 'My vertices', {'nest': None } } """ self.inputs = dict() def copy(self): result = SvDict(super().copy()) result.inputs = self.inputs return result @staticmethod def from_dict(d): if isinstance(d, SvDict): return d.copy() sv_dict = SvDict(d) inputs = {} for key, value in d.items(): if isinstance(value, SvDict): nested = value.inputs sock_type = 'SvDictionarySocket' elif isinstance(value, dict): nested = SvDict.from_dict(value).inputs sock_type = 'SvDictionarySocket' else: nested = None sock_type = 'SvStringsSocket' inputs[key] = { 'type': sock_type, 'name': key, 'nest': nested } sv_dict.inputs = inputs return sv_dict @staticmethod def get_inputs(d): if isinstance(d, SvDict) and d.inputs: return d.inputs else: inputs = {} for key in d: inputs[key] = { 'type': 'SvStringsSocket', 'name': key, 'nest': None } return inputs def get_max_nesting_level(self): max_level = 0 for key, value in self.items(): if isinstance(value, SvDict): level = value.get_max_nesting_level() + 1 elif isinstance(value, dict): level = SvDict(value).get_max_nesting_level() + 1 else: level = 0 if level > max_level: max_level = level return max_level def get_nested_keys_at(self, level): if level == 0: return set(self.keys()) else: keys = set() for value in self.values(): if isinstance(value, SvDict): v_keys = value.get_nested_keys_at(level-1) elif isinstance(value, dict): v_keys = SvDict(value).get_nested_keys_at(level-1) else: v_keys = set() keys.update(v_keys) return keys def get_nested_inputs_at(self, level): if level == 0: return SvDict.get_inputs(self) else: inputs = dict() for value in self.values(): if isinstance(value, SvDict): v_inputs = value.get_nested_inputs_at(level-1) elif isinstance(value, dict): v_inputs = SvDict(value).get_nested_inputs_at(level-1) else: v_inputs = None if v_inputs: inputs.update(v_inputs) return inputs def get_all_nested_keys(self): max_level = self.get_max_nesting_level() all_keys = [] for level in range(max_level): keys = self.get_nested_keys_at(level) all_keys.append(keys) return all_keys def get_sock_types(self, level=0): if level == 0: inputs = SvDict.get_inputs(self) return [input['type'] for input in inputs.values()] else: types = None for value in self.values(): if isinstance(value, SvDict): v_types = value.get_sock_types(level-1) elif isinstance(value, dict): v_types = SvDict(value).get_sock_types(level-1) else: v_types = None if v_types: if types is None: types = v_types else: for i, (old_type, new_type) in enumerate(zip_longest(types, v_types)): if old_type is None: types[i] = new_type elif old_type != new_type: types[i] = 'SvStringsSocket' return types
Ancestors
- builtins.dict
Static methods
def from_dict(d)
-
Expand source code
@staticmethod def from_dict(d): if isinstance(d, SvDict): return d.copy() sv_dict = SvDict(d) inputs = {} for key, value in d.items(): if isinstance(value, SvDict): nested = value.inputs sock_type = 'SvDictionarySocket' elif isinstance(value, dict): nested = SvDict.from_dict(value).inputs sock_type = 'SvDictionarySocket' else: nested = None sock_type = 'SvStringsSocket' inputs[key] = { 'type': sock_type, 'name': key, 'nest': nested } sv_dict.inputs = inputs return sv_dict
def get_inputs(d)
-
Expand source code
@staticmethod def get_inputs(d): if isinstance(d, SvDict) and d.inputs: return d.inputs else: inputs = {} for key in d: inputs[key] = { 'type': 'SvStringsSocket', 'name': key, 'nest': None } return inputs
Methods
def copy(self)
-
D.copy() -> a shallow copy of D
Expand source code
def copy(self): result = SvDict(super().copy()) result.inputs = self.inputs return result
def get_all_nested_keys(self)
-
Expand source code
def get_all_nested_keys(self): max_level = self.get_max_nesting_level() all_keys = [] for level in range(max_level): keys = self.get_nested_keys_at(level) all_keys.append(keys) return all_keys
def get_max_nesting_level(self)
-
Expand source code
def get_max_nesting_level(self): max_level = 0 for key, value in self.items(): if isinstance(value, SvDict): level = value.get_max_nesting_level() + 1 elif isinstance(value, dict): level = SvDict(value).get_max_nesting_level() + 1 else: level = 0 if level > max_level: max_level = level return max_level
def get_nested_inputs_at(self, level)
-
Expand source code
def get_nested_inputs_at(self, level): if level == 0: return SvDict.get_inputs(self) else: inputs = dict() for value in self.values(): if isinstance(value, SvDict): v_inputs = value.get_nested_inputs_at(level-1) elif isinstance(value, dict): v_inputs = SvDict(value).get_nested_inputs_at(level-1) else: v_inputs = None if v_inputs: inputs.update(v_inputs) return inputs
def get_nested_keys_at(self, level)
-
Expand source code
def get_nested_keys_at(self, level): if level == 0: return set(self.keys()) else: keys = set() for value in self.values(): if isinstance(value, SvDict): v_keys = value.get_nested_keys_at(level-1) elif isinstance(value, dict): v_keys = SvDict(value).get_nested_keys_at(level-1) else: v_keys = set() keys.update(v_keys) return keys
def get_sock_types(self, level=0)
-
Expand source code
def get_sock_types(self, level=0): if level == 0: inputs = SvDict.get_inputs(self) return [input['type'] for input in inputs.values()] else: types = None for value in self.values(): if isinstance(value, SvDict): v_types = value.get_sock_types(level-1) elif isinstance(value, dict): v_types = SvDict(value).get_sock_types(level-1) else: v_types = None if v_types: if types is None: types = v_types else: for i, (old_type, new_type) in enumerate(zip_longest(types, v_types)): if old_type is None: types[i] = new_type elif old_type != new_type: types[i] = 'SvStringsSocket' return types