Juggler
Juggling algorithms and event processing using gaudi framework
GroupBy.hpp
Go to the documentation of this file.
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
12 
13 #include <algorithm>
14 #include <iterator>
15 #include <utility>
16 
17 namespace Jug {
18 
19 /// Proxy for iterating over groups of elements within a container.
20 ///
21 /// @note Each group will contain at least one element.
22 ///
23 /// Consecutive elements with the same key (as defined by the KeyGetter) are
24 /// placed in one group. The proxy should always be used as part of a
25 /// range-based for loop. In combination with structured bindings to reduce the
26 /// boilerplate, the group iteration can be written as
27 ///
28 /// for (auto&& [key, elements] : GroupBy<...>(...)) {
29 /// // do something with just the key
30 /// ...
31 ///
32 /// // iterate over the group elements
33 /// for (const auto& element : elements) {
34 /// ...
35 /// }
36 /// }
37 ///
38 template <typename Iterator, typename KeyGetter>
39 class GroupBy {
40  public:
41  /// The key type that identifies elements within a group.
42  using Key = std::decay_t<decltype(KeyGetter()(*Iterator()))>;
43  /// A Group is an iterator range with the associated key.
44  using Group = std::pair<Key, Range<Iterator>>;
45  /// Iterator type representing the end of the groups.
46  ///
47  /// The end iterator will not be dereferenced in C++17 range-based loops. It
48  /// can thus be a simpler type without the overhead of the full group iterator
49  /// below.
50  using GroupEndIterator = Iterator;
51  /// Iterator type representing a group of elements.
52  class GroupIterator {
53  public:
54  using iterator_category = std::input_iterator_tag;
55  using value_type = Group;
56  using difference_type = std::ptrdiff_t;
57  using pointer = Group*;
58  using reference = Group&;
59 
60  constexpr GroupIterator(const GroupBy& groupBy, Iterator groupBegin)
61  : m_groupBy(groupBy),
62  m_groupBegin(groupBegin),
63  m_groupEnd(groupBy.findEndOfGroup(groupBegin)) {}
64  /// Pre-increment operator to advance to the next group.
65  constexpr GroupIterator& operator++() {
66  // make the current end the new group beginning
67  std::swap(m_groupBegin, m_groupEnd);
68  // find the end of the next group starting from the new beginning
69  m_groupEnd = m_groupBy.findEndOfGroup(m_groupBegin);
70  return *this;
71  }
72  /// Post-increment operator to advance to the next group.
73  constexpr GroupIterator operator++(int) {
74  GroupIterator retval = *this;
75  ++(*this);
76  return retval;
77  }
78  /// Derefence operator that returns the pointed-to group of elements.
79  constexpr Group operator*() const {
80  const Key key = (m_groupBegin != m_groupEnd)
81  ? m_groupBy.m_keyGetter(*m_groupBegin)
82  : Key();
83  return {key, makeRange(m_groupBegin, m_groupEnd)};
84  }
85 
86  private:
87  const GroupBy& m_groupBy;
88  Iterator m_groupBegin;
89  Iterator m_groupEnd;
90 
91  friend constexpr bool operator==(const GroupIterator& lhs,
92  const GroupEndIterator& rhs) {
93  return lhs.m_groupBegin == rhs;
94  }
95  friend constexpr bool operator!=(const GroupIterator& lhs,
96  const GroupEndIterator& rhs) {
97  return not(lhs == rhs);
98  }
99  };
100 
101  /// Construct the group-by proxy for an iterator range.
102  constexpr GroupBy(Iterator begin, Iterator end,
103  KeyGetter keyGetter = KeyGetter())
104  : m_begin(begin), m_end(end), m_keyGetter(std::move(keyGetter)) {}
105  constexpr GroupIterator begin() const {
106  return GroupIterator(*this, m_begin);
107  }
108  constexpr GroupEndIterator end() const { return m_end; }
109  constexpr bool empty() const { return m_begin == m_end; }
110 
111  private:
112  Iterator m_begin;
113  Iterator m_end;
114  KeyGetter m_keyGetter;
115 
116  /// Find the end of the group that starts at the given position.
117  ///
118  /// This uses a linear search from the start position and thus has linear
119  /// complexity in the group size. It does not assume any ordering of the
120  /// underlying container and is a cache-friendly access pattern.
121  constexpr Iterator findEndOfGroup(Iterator start) const {
122  // check for end so we can safely dereference the start iterator.
123  if (start == m_end) {
124  return start;
125  }
126  // search the first element that does not share a key with the start.
127  return std::find_if_not(std::next(start), m_end,
128  [this, start](const auto& x) {
129  return m_keyGetter(x) == m_keyGetter(*start);
130  });
131  }
132 };
133 
134 /// Construct the group-by proxy for a container.
135 template <typename Container, typename KeyGetter>
136 GroupBy<typename Container::const_iterator, KeyGetter> makeGroupBy(
137  const Container& container, KeyGetter keyGetter) {
138  return {container.begin(), container.end(), std::move(keyGetter)};
139 }
140 
141 } // namespace FW
Jug::GroupBy::GroupEndIterator
Iterator GroupEndIterator
Definition: GroupBy.hpp:50
Jug::GroupBy::GroupIterator::operator!=
constexpr friend bool operator!=(const GroupIterator &lhs, const GroupEndIterator &rhs)
Definition: GroupBy.hpp:95
Jug::makeGroupBy
auto makeGroupBy(const Container &container, KeyGetter keyGetter) -> GroupBy< decltype(std::begin(container)), KeyGetter >
Construct the group-by proxy for a container.
Definition: GroupBy.hpp:136
example_calodigi.key
key
Definition: example_calodigi.py:28
Jug::GroupBy::GroupIterator::value_type
Group value_type
Definition: GroupBy.hpp:55
Jug::GroupBy::empty
constexpr bool empty() const
Definition: GroupBy.hpp:109
Jug::GroupBy::begin
constexpr GroupIterator begin() const
Definition: GroupBy.hpp:105
Jug::GroupBy::GroupIterator::difference_type
std::ptrdiff_t difference_type
Definition: GroupBy.hpp:56
Jug::GroupBy::end
constexpr GroupEndIterator end() const
Definition: GroupBy.hpp:108
Jug::GroupBy::GroupIterator
Iterator type representing a group of elements.
Definition: GroupBy.hpp:52
Jug::GroupBy::GroupBy
constexpr GroupBy(Iterator begin, Iterator end, KeyGetter keyGetter=KeyGetter())
Construct the group-by proxy for an iterator range.
Definition: GroupBy.hpp:102
Range.hpp
Jug::GroupBy::GroupIterator::pointer
Group * pointer
Definition: GroupBy.hpp:57
Jug
Definition: DD4hepBField.h:22
Jug::GroupBy
Definition: GroupBy.hpp:39
Jug::GroupBy::GroupIterator::operator*
constexpr Group operator*() const
Derefence operator that returns the pointed-to group of elements.
Definition: GroupBy.hpp:79
Jug::makeRange
Range< Iterator > makeRange(Iterator begin, Iterator end)
Definition: Range.hpp:47
Jug::GroupBy::GroupIterator::operator++
constexpr GroupIterator operator++(int)
Post-increment operator to advance to the next group.
Definition: GroupBy.hpp:73
Jug::GroupBy::GroupIterator::iterator_category
std::input_iterator_tag iterator_category
Definition: GroupBy.hpp:54
Jug::GroupBy::Group
std::pair< Key, Range< Iterator > > Group
A Group is an iterator range with the associated key.
Definition: GroupBy.hpp:44
Jug::GroupBy::Key
std::decay_t< decltype(KeyGetter()(*Iterator()))> Key
The key type that identifies elements within a group.
Definition: GroupBy.hpp:42
Jug::GroupBy::GroupIterator::GroupIterator
constexpr GroupIterator(const GroupBy &groupBy, Iterator groupBegin)
Definition: GroupBy.hpp:60
Jug::GroupBy::GroupIterator::reference
Group & reference
Definition: GroupBy.hpp:58
Jug::GroupBy::GroupIterator::operator==
constexpr friend bool operator==(const GroupIterator &lhs, const GroupEndIterator &rhs)
Definition: GroupBy.hpp:91
Jug::GroupBy::GroupIterator::operator++
constexpr GroupIterator & operator++()
Pre-increment operator to advance to the next group.
Definition: GroupBy.hpp:65