ForEachAssociatedGroup.h
Go to the documentation of this file.
1 /**
2  * @file lardata/Utilities/ForEachAssociatedGroup.h
3  * @brief Helper functions to access associations in order.
4  *
5  * No additional linking is required to use these functions.
6  *
7  * Provided functions:
8  *
9  * * `util::associated_groups()` providing a sequence of objects associated to
10  * the same object, for each object
11  *
12  */
13 
14 
15 #ifndef LARDATA_UTILITIES_FOR_EACH_ASSOCIATED_GROUP_H
16 #define LARDATA_UTILITIES_FOR_EACH_ASSOCIATED_GROUP_H
17 
18 // LArSoft libraries
20 
21 // framework libraries
22 #include "canvas/Persistency/Common/AssnsAlgorithms.h" // art::for_each_group()
23 
24 // range library
25 #include "range/v3/algorithm/for_each.hpp"
26 #include "range/v3/view/group_by.hpp"
27 #include "range/v3/view/transform.hpp"
28 #include "range/v3/view/map.hpp" // range::views::values
29 #include "range/v3/view/all.hpp"
30 
31 // C/C++ standard libraries
32 #include <utility> // std::make_pair()
33 #include <iterator> // std::next()
34 
35 
36 namespace util {
37  /**
38  * @brief Helper functions to access associations in order.
39  * @tparam A type of association being read
40  * @tparam F type of functor to be called on each associated group
41  * @param assns the association being read
42  * @param func functor to be called on each associated group
43  * @see associated_groups() art::for_each_group()
44  *
45  * @deprecated Moved into _canvas_: `art::for_each_group()`.
46  */
47  template <class A, class F>
48  [[deprecated("Use art::for_each_group() instead")]]
49  void for_each_associated_group(A const & assns, F & func)
50  { art::for_each_group(assns, func); }
51 
52 
53  /**
54  * @brief Helper functions to access associations in order.
55  * @tparam A type of association being read
56  * @param assns the association being read
57  * @see for_each_associated_group()
58  *
59  * This function provides a functionality equivalent to
60  * `art::for_each_group()`, but it grants the caller additional control on the
61  * external loop and on the function.
62  *
63  * Example: assuming that a module with input tag stored in `fTrackTag` has
64  * created associations of each track to its hits, the total charge for each
65  * track can be extracted by:
66  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
67  * auto assns = art::getValidHandle<art::Assns<recob::Track, recob::Hit>>
68  * (fTrackTag);
69  *
70  * std::vector<double> totalCharge;
71  *
72  * for (auto const& hits: util::associated_groups(*assns)) {
73  * double total = 0.;
74  * for (art::Ptr<recob::Hit> const& hit: hits)
75  * total += hit->Integral();
76  * totalCharge.push_back(total);
77  * } // for
78  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79  * A number of important points need to be realised about this example:
80  *
81  * * the requirements of this function on its input association are the same
82  * as for `art::for_each_group()`
83  * * we can code the action on each group of hits directly in a loop, if
84  * like in this case the code is succinct
85  * * again, there is one outer loop iteration for every track;
86  * * the value of `hits` is an object representing a range of _art_ pointers
87  * (`art::Ptr<recob::Hit>`) which can be navigated with the
88  * `begin()`/`end()` free functions, or in a range-for loop;
89  * * on each iteration, the information of which track the hits are
90  * associated to is not available; if that is also needed, use
91  * `util::associated_groups_with_left()` instead.
92  */
93  template <class A>
94  auto associated_groups(A const & assns) {
95  return assns |
97  ranges::views::group_by([](auto a1, auto a2) { return a1.first == a2.first;}) |
98  ranges::views::transform([] (auto pairs) {return pairs | ranges::views::values | util::range_for;}) |
100  ;
101  } // associated_groups()
102 
103 
104  /**
105  * @brief Helper functions to access associations in order, also with key.
106  * @tparam A type of association being read
107  * @param assns the association being read
108  * @see for_each_associated_group()
109  *
110  * This function provides a functionality equivalent to
111  * `art::for_each_group_with_left()`, but it grants the caller additional
112  * control on the external loop and on the function.
113  *
114  * Example: assuming that a module with input tag stored in `fTrackTag` has
115  * created associations of each track to its hits, the total charge for each
116  * track can be extracted by:
117  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
118  * auto assns = art::getValidHandle<art::Assns<recob::Track, recob::Hit>>
119  * (fTrackTag);
120  *
121  * std::map<int, double> totalCharge;
122  *
123  * for (auto const& trackWithHits: util::associated_groups_with_left(*assns))
124  * {
125  * art::Ptr<recob::Track> const& track = trackWithHits.first;
126  * auto const& hits = trackWithHits.second;
127  *
128  * if (totalCharge.count(track->ID()) > 0) {
129  * throw art::Exception(art::errors::LogicError)
130  * << "Multiple tracks have ID " << track->ID() << "!\n";
131  * }
132  *
133  * double& total = totalCharge[track->ID()];
134  * total = 0.0;
135  * for (art::Ptr<recob::Hit> const& hit: hits)
136  * total += hit->Integral();
137  *
138  * } // for
139  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140  * A number of important points need to be realised about this example:
141  *
142  * * the requirements of this function on its input association are the same
143  * as for `art::for_each_group_with_left()`
144  * * we can code the action on each group of hits directly in a loop, if
145  * like in this case the code is succinct
146  * * again, there is one outer loop iteration for every track;
147  * * the value of `hits` is an object representing a range of _art_ pointers
148  * (`art::Ptr<recob::Hit>`) which can be navigated with the
149  * `begin()`/`end()` free functions, or in a range-for loop.
150  */
151  template <class A>
152  auto associated_groups_with_left(A const & assns) {
153  return assns
155  | ranges::views::group_by([](auto a1, auto a2) { return a1.first == a2.first;})
156  | ranges::views::transform([] (auto pairs)
157  {
158  return std::make_pair(
159  pairs.front().first, // assuming they're all the same, pick first
161  );
162  })
164  ;
165  } // associated_groups_with_left()
166 
167 
168  /**
169  * @brief Returns the group within `groups` with the specified index.
170  * @tparam Groups the type of collection of groups
171  * @param groups the collection of all groups
172  * @param index the index of the group to be accessed
173  * @return the group with specified index (may be a reference)
174  * @see `associated_groups()`
175  *
176  * The `groups` argument is expected to be the one returned by
177  * `associated_groups`.
178  */
179  template <typename Groups>
180  auto groupByIndex(Groups&& groups, std::size_t index) -> decltype(auto)
181  { return *(std::next(groups.begin(), index)); }
182 
183 } // namespace util
184 
185 #endif // LARDATA_UTILITIES_FOR_EACH_ASSOCIATED_GROUP_H
auto associated_groups_with_left(A const &assns)
Helper functions to access associations in order, also with key.
Namespace for general, non-LArSoft-specific utilities.
void for_each_associated_group(A const &assns, F &func)
Helper functions to access associations in order.
Utility function to enable range-for on different type iterators.
auto groupByIndex(Groups &&groups, std::size_t index) -> decltype(auto)
Returns the group within groups with the specified index.
#define a2
auto associated_groups(A const &assns)
Helper functions to access associations in order.
constexpr RangeForWrapperTag range_for
Q_UINT16 values[128]
static QInternalList< QTextCodec > * all
Definition: qtextcodec.cpp:63
void for_each_group(art::Assns< A, B, D > const &assns, F func)
Helper functions to access associations in order.
def func()
Definition: docstring.py:7
def group_by(rflist, field)
Definition: __init__.py:162
#define a1