Skip to content

Commit e93be5c

Browse files
joaquimgodow
andauthored
[Utilities] add DoubleDicts.outer_keys (#2052)
* DoubleDict outer_keys and nonempty_outer_keys Co-authored-by: Oscar Dowson <[email protected]>
1 parent 70c7a1b commit e93be5c

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

docs/src/submodules/Utilities/reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,6 @@ Utilities.DoubleDicts.DoubleDict
220220
Utilities.DoubleDicts.DoubleDictInner
221221
Utilities.DoubleDicts.IndexDoubleDict
222222
Utilities.DoubleDicts.IndexDoubleDictInner
223+
Utilities.DoubleDicts.outer_keys
224+
Utilities.DoubleDicts.nonempty_outer_keys
223225
```

src/Utilities/DoubleDicts.jl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,55 @@ function Base.keys(d::AbstractDoubleDictInner{F,S}) where {F,S}
299299
return MOI.ConstraintIndex{F,S}.(keys(d.dict))
300300
end
301301

302+
"""
303+
outer_keys(d::AbstractDoubleDict)
304+
305+
Return an iterator over the outer keys of the `AbstractDoubleDict` `d`.
306+
Each outer key is a `Tuple{Type,Type}` so that a double loop can
307+
be easily used:
308+
```julia
309+
for (F, S) in DoubleDicts.outer_keys(dict)
310+
for (k, v) in dict[F, S]
311+
# ...
312+
end
313+
end
314+
```
315+
316+
For performance, it is recommended that the inner loop lies in a separate
317+
function to gurantee type-stability.
318+
Some outer keys `(F, S)` might lead to an empty `dict[F, S]`.
319+
If you want only nonempty `dict[F, S]`, use [`nonempty_outer_keys`](@ref).
320+
"""
321+
outer_keys(d::AbstractDoubleDict) = keys(d.dict)
322+
323+
"""
324+
nonempty_outer_keys(d::AbstractDoubleDict)
325+
326+
Return a vector of outer keys of the `AbstractDoubleDict` `d`.
327+
328+
Only outer keys that have a nonempty set of inner keys will be returned.
329+
330+
Each outer key is a `Tuple{Type,Type}` so that a double loop can
331+
be easily used
332+
```julia
333+
for (F, S) in DoubleDicts.nonempty_outer_keys(dict)
334+
for (k, v) in dict[F, S]
335+
# ...
336+
end
337+
end
338+
For performance, it is recommended that the inner loop lies in a separate
339+
function to gurantee type-stability.
340+
341+
If you want an iterator of all current outer keys, use [`outer_keys`](@ref).
342+
```
343+
"""
344+
function nonempty_outer_keys(d::AbstractDoubleDict)
345+
return Base.Iterators.Filter(
346+
fs -> length(d[fs[1], fs[2]]) > 0,
347+
outer_keys(d),
348+
)
349+
end
350+
302351
# Base.iterate
303352

304353
function Base.iterate(d::AbstractDoubleDict)

test/Utilities/DoubleDicts.jl

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,83 @@ function test_IndexDoubleDict()
177177
return
178178
end
179179

180+
function test_IndexDoubleDict_double_loop()
181+
dict = DoubleDicts.IndexDoubleDict()
182+
keys = [
183+
MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}(1),
184+
MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}(2),
185+
MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(1),
186+
]
187+
vals = keys
188+
for (k, v) in zip(keys, vals)
189+
dict[k] = v
190+
end
191+
# outer_keys
192+
number_elements = 0
193+
number_outer_pairs = 0
194+
for (F, S) in DoubleDicts.outer_keys(dict)
195+
number_outer_pairs += 1
196+
for (k, v) in dict[F, S]
197+
@test dict[k] == v
198+
number_elements += 1
199+
end
200+
end
201+
@test number_elements == 3
202+
@test number_outer_pairs == 2
203+
# nonempty_outer_keys
204+
number_elements = 0
205+
number_outer_pairs = 0
206+
for (F, S) in DoubleDicts.nonempty_outer_keys(dict)
207+
number_outer_pairs += 1
208+
for (k, v) in dict[F, S]
209+
@test dict[k] == v
210+
number_elements += 1
211+
end
212+
end
213+
@test number_elements == 3
214+
@test number_outer_pairs == 2
215+
return
216+
end
217+
218+
function test_IndexDoubleDict_double_loop_with_delete()
219+
dict = DoubleDicts.IndexDoubleDict()
220+
keys = [
221+
MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}(1),
222+
MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}(2),
223+
MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(1),
224+
]
225+
vals = keys
226+
for (k, v) in zip(keys, vals)
227+
dict[k] = v
228+
end
229+
delete!(dict, MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(1))
230+
# outer_keys
231+
number_elements = 0
232+
number_outer_pairs = 0
233+
for (F, S) in DoubleDicts.outer_keys(dict)
234+
number_outer_pairs += 1
235+
for (k, v) in dict[F, S]
236+
@test dict[k] == v
237+
number_elements += 1
238+
end
239+
end
240+
@test number_elements == 2
241+
@test number_outer_pairs == 2
242+
# nonempty_outer_keys
243+
number_elements = 0
244+
number_outer_pairs = 0
245+
for (F, S) in DoubleDicts.nonempty_outer_keys(dict)
246+
number_outer_pairs += 1
247+
for (k, v) in dict[F, S]
248+
@test dict[k] == v
249+
number_elements += 1
250+
end
251+
end
252+
@test number_elements == 2
253+
@test number_outer_pairs == 1
254+
return
255+
end
256+
180257
end # module
181258

182259
TestDoubleDicts.runtests()

0 commit comments

Comments
 (0)