Skip to content

Commit 482ef8d

Browse files
committed
enable selection init by dict
1 parent 724e6bf commit 482ef8d

2 files changed

Lines changed: 77 additions & 2 deletions

File tree

src/h5json/selections.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ def select(obj, args, fields=None):
6565
Indices, slices, ellipses, lists or boolean index arrays
6666
Returns a SimpleSelection instance with H5S_SEL_FANCY.
6767
"""
68-
if not isinstance(args, tuple):
69-
args = (args,)
7068

7169
if hasattr(obj, "shape"):
7270
obj_shape = obj.shape
@@ -75,6 +73,11 @@ def select(obj, args, fields=None):
7573
else:
7674
raise TypeError("Object must be a dataset or a shape tuple")
7775

76+
if isinstance(args, dict):
77+
args = _handle_dict_selection(obj_shape, args)
78+
elif not isinstance(args, tuple):
79+
args = (args,)
80+
7881
if len(obj_shape) == 0:
7982
# scalar object
8083
sel = ScalarSelection(obj_shape, args)
@@ -829,6 +832,57 @@ def translate(s1, s2):
829832
return select(s1.shape, tuple(args))
830833

831834

835+
def _handle_dict_selection(shape, arg):
836+
""" Handle a dictionary-based selection, where the keys are dimension indices and
837+
the values are slices or lists of coordinates. Returns a tuple of slices/lists for
838+
each dimension, filling in full slices for unspecified dimensions.
839+
"""
840+
rank = len(shape)
841+
slices = []
842+
if "start" not in arg:
843+
start = (0,) * rank
844+
else:
845+
start = arg["start"]
846+
if "stop" not in arg:
847+
stop = shape
848+
else:
849+
stop = arg["stop"]
850+
if "step" not in arg:
851+
step = (1,) * rank
852+
else:
853+
step = arg["step"]
854+
855+
if isinstance(start, int):
856+
start = (start,) * rank # broadcast to all dimensions
857+
elif isinstance(start, (list, tuple)):
858+
if len(start) != rank:
859+
raise ValueError("Start list length does not match dataset rank")
860+
else:
861+
raise TypeError("Start value must be an int or a list/tuple of ints")
862+
863+
if isinstance(stop, int):
864+
stop = (stop,) * rank # broadcast to all dimensions
865+
elif isinstance(stop, (list, tuple)):
866+
if len(stop) != rank:
867+
raise ValueError("Stop list length does not match dataset rank")
868+
else:
869+
raise TypeError("Stop value must be an int or a list/tuple of ints")
870+
871+
if isinstance(step, int):
872+
step = (step,) * rank # broadcast to all dimensions
873+
elif isinstance(step, (list, tuple)):
874+
if len(step) != rank:
875+
raise ValueError("Step list length does not match dataset rank")
876+
else:
877+
raise TypeError("Step value must be an int or a list/tuple of ints")
878+
879+
for idx in range(rank):
880+
s = slice(start[idx], stop[idx], step[idx])
881+
slices.append(s)
882+
883+
return slices
884+
885+
832886
class Selection(object):
833887

834888
"""

test/unit/selection_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ def testSelectAll(self):
6060
self.assertEqual(sel, sel2)
6161
self.assertEqual(sel.query_string, query_string)
6262

63+
# create the same selection from a dict
64+
sel3 = selections.select(shape, {})
65+
self.assertEqual(sel, sel3)
66+
6367
def testSelectAll2D(self):
6468
shape = (4, 5)
6569
sel = selections.select(shape, ...)
@@ -80,6 +84,10 @@ def testSelectAll2D(self):
8084
self.assertEqual(sel, sel2)
8185
self.assertEqual(sel.query_string, query_string)
8286

87+
# create the same selection from a dict
88+
sel3 = selections.select(shape, {})
89+
self.assertEqual(sel, sel3)
90+
8391
def testSlice1D(self):
8492
shape = (10,)
8593
sel = selections.select(shape, slice(2, 7))
@@ -102,6 +110,10 @@ def testSlice1D(self):
102110
self.assertEqual(sel, sel2)
103111
self.assertEqual(sel.query_string, query_string)
104112

113+
# create the same selection from a dict
114+
sel3 = selections.select(shape, {"start": 2, "stop": 7})
115+
self.assertEqual(sel, sel3)
116+
105117
def testSliceWithStep(self):
106118
shape = (10,)
107119
sel = selections.select(shape, slice(0, 10, 2))
@@ -124,6 +136,10 @@ def testSliceWithStep(self):
124136
self.assertEqual(sel, sel2)
125137
self.assertEqual(sel.query_string, query_string)
126138

139+
# create the same selection from a dict
140+
sel3 = selections.select(shape, {"start": 0, "stop": 10, "step": 2})
141+
self.assertEqual(sel, sel3)
142+
127143
def testSlice2D(self):
128144
shape = (8, 10)
129145
sel = selections.select(shape, (slice(1, 4), slice(2, 9)))
@@ -146,6 +162,10 @@ def testSlice2D(self):
146162
self.assertEqual(sel, sel2)
147163
self.assertEqual(sel.query_string, query_string)
148164

165+
# create the same selection from a dict
166+
sel3 = selections.select(shape, {"start": [1, 2], "stop": [4, 9]})
167+
self.assertEqual(sel, sel3)
168+
149169
def testBroadcast1D(self):
150170
shape = (10,)
151171
sel = selections.select(shape, ...)
@@ -388,6 +408,7 @@ def testFancyCoord(self):
388408
self.assertEqual(len(slices), 2)
389409
self.assertEqual(slices[0], slice(0, 5, 1))
390410
self.assertEqual(slices[1], [3, 7])
411+
391412
# create the same selection from a string
392413
query_string = "[0:5,[3,7]]"
393414
sel2 = selections.select(shape, query_string)

0 commit comments

Comments
 (0)