Skip to content

Commit daf8541

Browse files
added method to test for duplicates
1 parent b4e9ba1 commit daf8541

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

tests/test_vdf_dict.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,20 @@ def test_remove_all_for_invalid_key(self):
275275
with self.assertRaises(TypeError):
276276
a.remove_all_for((0, '5'))
277277

278+
def test_has_duplicates(self):
279+
# single level duplicate
280+
a = [('1', 11), ('1', 22)]
281+
b = VDFDict(a)
282+
self.assertTrue(b.has_duplicates())
283+
284+
# duplicate in nested
285+
c = VDFDict({'1': b})
286+
self.assertTrue(c.has_duplicates())
287+
288+
# duplicate in nested dict
289+
d = VDFDict({'1': {'2': {'3': b}}})
290+
self.assertTrue(d.has_duplicates())
291+
292+
# duplicate in nested dict
293+
d = VDFDict({'1': {'2': {'3': None}}})
294+
self.assertFalse(d.has_duplicates())

vdf/vdict.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
from collections import Counter
33

44
if sys.version_info[0] >= 3:
5+
_iter_values = 'values'
56
_range = range
6-
string_type = str
7+
_string_type = str
78
import collections as _c
89
class _kView(_c.KeysView):
910
def __iter__(self):
@@ -15,8 +16,9 @@ class _iView(_c.ItemsView):
1516
def __iter__(self):
1617
return self._mapping.iteritems()
1718
else:
19+
_iter_values = 'itervalues'
1820
_range = xrange
19-
string_type = basestring
21+
_string_type = basestring
2022
_kView = lambda x: list(x.iterkeys())
2123
_vView = lambda x: list(x.itervalues())
2224
_iView = lambda x: list(x.iteritems())
@@ -56,11 +58,11 @@ def _verify_key_tuple(self, key):
5658
raise ValueError("Expected key tuple length to be 2, got %d" % len(key))
5759
if not isinstance(key[0], int):
5860
raise TypeError("Key index should be an int")
59-
if not isinstance(key[1], string_type):
61+
if not isinstance(key[1], _string_type):
6062
raise TypeError("Key value should be a str")
6163

6264
def _normalize_key(self, key):
63-
if isinstance(key, string_type):
65+
if isinstance(key, _string_type):
6466
key = (0, key)
6567
elif isinstance(key, tuple):
6668
self._verify_key_tuple(key)
@@ -69,7 +71,7 @@ def _normalize_key(self, key):
6971
return key
7072

7173
def __setitem__(self, key, value):
72-
if isinstance(key, string_type):
74+
if isinstance(key, _string_type):
7375
key = (self.__kcount[key], key)
7476
self.__omap.append(key)
7577
elif isinstance(key, tuple):
@@ -183,13 +185,13 @@ def items(self):
183185

184186
def get_all_for(self, key):
185187
""" Returns all values of the given key """
186-
if not isinstance(key, string_type):
188+
if not isinstance(key, _string_type):
187189
raise TypeError("Key needs to be a string.")
188190
return [self[(idx, key)] for idx in _range(self.__kcount[key])]
189191

190192
def remove_all_for(self, key):
191193
""" Removes all items with the given key """
192-
if not isinstance(key, string_type):
194+
if not isinstance(key, _string_type):
193195
raise TypeError("Key need to be a string.")
194196

195197
for idx in _range(self.__kcount[key]):
@@ -198,3 +200,22 @@ def remove_all_for(self, key):
198200
self.__omap = list(filter(lambda x: x[1] != key, self.__omap))
199201

200202
del self.__kcount[key]
203+
204+
def has_duplicates(self):
205+
"""
206+
Returns ``True`` if the dict contains keys with duplicates.
207+
Recurses through any all keys with value that is ``VDFDict``.
208+
"""
209+
for n in getattr(self.__kcount, _iter_values)():
210+
if n != 1:
211+
return True
212+
213+
def dict_recurse(obj):
214+
for v in getattr(obj, _iter_values)():
215+
if isinstance(v, VDFDict) and v.has_duplicates():
216+
return True
217+
elif isinstance(v, dict):
218+
return dict_recurse(v)
219+
return False
220+
221+
return dict_recurse(self)

0 commit comments

Comments
 (0)