Skip to content

Commit

Permalink
Make lists containing dicts hashable. (#218)
Browse files Browse the repository at this point in the history
Fixes issue #217.
  • Loading branch information
csadorf committed Aug 9, 2019
1 parent b100fc0 commit add19c3
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 5 deletions.
17 changes: 12 additions & 5 deletions signac/contrib/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,16 @@ def _flatten(container):
yield i


def _to_tuples(l):
class _hashable_dict(dict):
def __hash__(self):
return hash(tuple(sorted(self.items())))


def _to_hashable(l):
if type(l) is list:
return tuple(_to_tuples(_) for _ in l)
return tuple(_to_hashable(_) for _ in l)
elif type(l) is dict:
return _hashable_dict(l)
else:
return l

Expand All @@ -79,7 +86,7 @@ class _DictPlaceholder(object):

def _encode_tree(x):
if type(x) is list:
return _to_tuples(x)
return _to_hashable(x)
else:
return x

Expand Down Expand Up @@ -190,7 +197,7 @@ def _build_index(docs, key, primary_key):
if type(v) is dict:
continue
elif type(v) is list: # performance
index[_to_tuples(v)].add(doc[primary_key])
index[_to_hashable(v)].add(doc[primary_key])
else:
index[v].add(doc[primary_key])

Expand All @@ -208,7 +215,7 @@ def _build_index(docs, key, primary_key):
"for a recipe on how to replace dots in existing keys.")
# inlined for performance
if type(v) is list: # performance
index[_to_tuples(v)].add(doc[primary_key])
index[_to_hashable(v)].add(doc[primary_key])
else:
index[v].add(doc[primary_key])
return index
Expand Down
11 changes: 11 additions & 0 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,17 @@ def test_find_float(self):
del self.c[docs[0]['_id']]
self.assertEqual(len(self.c.find({'a': 0})), 0)

def test_find_list(self):
self.assertEqual(len(self.c.find()), 0)
self.assertEqual(list(self.c.find()), [])
self.assertEqual(len(self.c.find({'a': []})), 0)
self.c.insert_one({'a': []})
self.assertEqual(len(self.c.find()), 1)
self.assertEqual(len(self.c.find({'a': []})), 1)
for v in (None, 1, '1', {'b': 1}):
self.c.insert_one({'a': [v]})
self.assertEqual(len(self.c.find({'a': [v]})), 1)

def test_find_int_float(self):
id_float = self.c.insert_one({'a': float(1.0)})
id_int = self.c.insert_one({'a': 1})
Expand Down

0 comments on commit add19c3

Please sign in to comment.