Skip to content

Commit 5f5e386

Browse files
authored
Multi-dimensional array added (#256)
1 parent 2284b55 commit 5f5e386

File tree

4 files changed

+161
-10
lines changed

4 files changed

+161
-10
lines changed

pydatastructs/linear_data_structures/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
from .arrays import (
1010
OneDimensionalArray,
11-
DynamicOneDimensionalArray
11+
DynamicOneDimensionalArray,
12+
MultiDimensionalArray
1213
)
1314
__all__.extend(arrays.__all__)
1415

pydatastructs/linear_data_structures/arrays.py

Lines changed: 124 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
from pydatastructs.utils.misc_util import _check_type, NoneType
22

33
__all__ = [
4-
'OneDimensionalArray',
5-
'DynamicOneDimensionalArray'
4+
'OneDimensionalArray',
5+
'MultiDimensionalArray',
6+
'DynamicOneDimensionalArray'
67
]
78

89
class Array(object):
910
'''
1011
Abstract class for arrays in pydatastructs.
1112
'''
12-
pass
13+
def __str__(self) -> str:
14+
return str(self._data)
1315

1416
class OneDimensionalArray(Array):
1517
'''
@@ -112,8 +114,7 @@ def __new__(cls, dtype=NoneType, *args, **kwargs):
112114
@classmethod
113115
def methods(cls):
114116
return ['__new__', '__getitem__',
115-
'__setitem__', 'fill', '__len__',
116-
'__str__']
117+
'__setitem__', 'fill', '__len__']
117118

118119
def __getitem__(self, i):
119120
if i >= self._size or i < 0:
@@ -136,9 +137,125 @@ def fill(self, elem):
136137
def __len__(self):
137138
return self._size
138139

139-
def __str__(self):
140-
return str(self._data)
140+
class MultiDimensionalArray(Array):
141+
"""
142+
Represents a multi-dimensional array.
143+
144+
Parameters
145+
==========
146+
147+
dtype: type
148+
A valid object type.
149+
*args: int
150+
The dimensions of the array.
151+
152+
Raises
153+
======
154+
155+
IndexError
156+
Index goes out of boundaries, or
157+
the number of index given is not
158+
the same as the number of dimensions.
159+
ValueError
160+
When there's no dimensions or the
161+
dimension size is 0.
162+
163+
Examples
164+
========
165+
166+
>>> from pydatastructs import MultiDimensionalArray as MDA
167+
>>> arr = MDA(int, 5, 6, 9)
168+
>>> arr.fill(32)
169+
>>> arr[3, 0, 0]
170+
32
171+
>>> arr[3, 0, 0] = 7
172+
>>> arr[3, 0, 0]
173+
7
174+
175+
References
176+
==========
177+
178+
.. [1] https://en.wikipedia.org/wiki/Array_data_structure#Multidimensional_arrays
179+
180+
"""
181+
__slots__ = ['_sizes', '_data', '_dtype']
182+
183+
def __new__(cls, dtype: type = NoneType, *args, **kwargs):
184+
if dtype is NoneType or not args:
185+
raise ValueError("Array cannot be created due to incorrect"
186+
" information.")
187+
if len(args) == 1:
188+
obj = Array.__new__(cls)
189+
obj._dtype = dtype
190+
obj._sizes = (args[0], 1)
191+
obj._data = [None] * args[0]
192+
return obj
193+
194+
dimensions = args
195+
for dimension in dimensions:
196+
if dimension < 1:
197+
raise ValueError("Array cannot be created due to incorrect"
198+
" dimensions.")
199+
n_dimensions = len(dimensions)
200+
d_sizes = []
201+
index = 0
202+
while n_dimensions > 1:
203+
size = dimensions[index]
204+
for i in range(index+1, len(dimensions)):
205+
size = size * dimensions[i]
206+
d_sizes.append(size)
207+
n_dimensions -= 1
208+
index += 1
209+
d_sizes.append(dimensions[index])
210+
d_sizes.append(1)
211+
obj = Array.__new__(cls)
212+
obj._dtype = dtype
213+
obj._sizes = tuple(d_sizes)
214+
obj._data = [None] * obj._sizes[1] * dimensions[0]
215+
return obj
141216

217+
@classmethod
218+
def methods(cls) -> list:
219+
return ['__new__', '__getitem__', '__setitem__', 'fill', 'shape']
220+
221+
def __getitem__(self, indices):
222+
self._compare_shape(indices)
223+
if isinstance(indices, int):
224+
return self._data[indices]
225+
position = 0
226+
for i in range(0, len(indices)):
227+
position += self._sizes[i + 1] * indices[i]
228+
return self._data[position]
229+
230+
def __setitem__(self, indices, element) -> None:
231+
self._compare_shape(indices)
232+
if isinstance(indices, int):
233+
self._data[indices] = element
234+
else:
235+
position = 0
236+
for i in range(0, len(indices)):
237+
position += self._sizes[i + 1] * indices[i]
238+
self._data[position] = element
239+
240+
def _compare_shape(self, indices) -> None:
241+
indices = [indices] if isinstance(indices, int) else indices
242+
if len(indices) != len(self._sizes) - 1:
243+
raise IndexError("Shape mismatch, current shape is %s" % str(self.shape))
244+
if any(indices[i] >= self._sizes[i] for i in range(len(indices))):
245+
raise IndexError("Index out of range.")
246+
247+
def fill(self, element) -> None:
248+
element = self._dtype(element)
249+
for i in range(len(self._data)):
250+
self._data[i] = element
251+
252+
@property
253+
def shape(self) -> tuple:
254+
shape = []
255+
size = len(self._sizes)
256+
for i in range(1, size):
257+
shape.append(self._sizes[i-1]//self._sizes[i])
258+
return tuple(shape)
142259

143260
class DynamicArray(Array):
144261
"""

pydatastructs/linear_data_structures/tests/test_arrays.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from pydatastructs.linear_data_structures import (
2-
OneDimensionalArray, DynamicOneDimensionalArray)
2+
OneDimensionalArray, DynamicOneDimensionalArray, MultiDimensionalArray)
33
from pydatastructs.utils.raises_util import raises
44

55

@@ -21,6 +21,39 @@ def test_OneDimensionalArray():
2121
assert raises(TypeError, lambda: ODA(int, set([1, 2, 3])))
2222
assert raises(ValueError, lambda: ODA(int, 3, [1]))
2323

24+
25+
def test_MultiDimensionalArray():
26+
assert raises(ValueError, lambda: MultiDimensionalArray(int, 2, -1, 3))
27+
assert MultiDimensionalArray(int, 10).shape == (10,)
28+
array = MultiDimensionalArray(int, 5, 9, 3, 8)
29+
assert array.shape == (5, 9, 3, 8)
30+
array.fill(5)
31+
array[1, 3, 2, 5] = 2.0
32+
assert array
33+
assert array[1, 3, 2, 5] == 2.0
34+
assert array[1, 3, 0, 5] == 5
35+
assert array[1, 2, 2, 5] == 5
36+
assert array[2, 3, 2, 5] == 5
37+
assert raises(IndexError, lambda: array[5])
38+
assert raises(IndexError, lambda: array[4, 10])
39+
assert raises(IndexError, lambda: array[-1])
40+
assert raises(IndexError, lambda: array[2, 3, 2, 8])
41+
assert raises(ValueError, lambda: MultiDimensionalArray())
42+
assert raises(ValueError, lambda: MultiDimensionalArray(int))
43+
assert raises(TypeError, lambda: MultiDimensionalArray(int, 5, 6, ""))
44+
array = MultiDimensionalArray(int, 3, 2, 2)
45+
array.fill(1)
46+
array[0, 0, 0] = 0
47+
array[0, 0, 1] = 0
48+
array[1, 0, 0] = 0
49+
array[2, 1, 1] = 0
50+
assert str(array) == '[0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0]'
51+
array = MultiDimensionalArray(int, 4)
52+
assert array.shape == (4,)
53+
array.fill(5)
54+
array[3] = 3
55+
assert array[3] == 3
56+
2457
def test_DynamicOneDimensionalArray():
2558
DODA = DynamicOneDimensionalArray
2659
A = DODA(int, 0)

pydatastructs/utils/tests/test_code_quality.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _apis():
7979
pyds.DoublyLinkedList, pyds.SinglyLinkedList,
8080
pyds.SinglyCircularLinkedList,
8181
pyds.DoublyCircularLinkedList,
82-
pyds.OneDimensionalArray,
82+
pyds.OneDimensionalArray, pyds.MultiDimensionalArray,
8383
pyds.DynamicOneDimensionalArray,
8484
pyds.trees.BinaryTree, pyds.BinarySearchTree,
8585
pyds.AVLTree, pyds.SplayTree, pyds.BinaryTreeTraversal,

0 commit comments

Comments
 (0)