Loading...
Searching...
No Matches
query.h
Go to the documentation of this file.
1
24
25#ifndef FCPP_QUERY_H
26#define FCPP_QUERY_H
27
28#include <algorithm>
29#include <cstddef>
30#include <functional>
31#include <iterator>
32#include <map>
33#include <numeric>
34#include <optional>
35#include <random>
36#include <set>
37#include <stdexcept>
38#include <string>
39#include <tuple>
40#include <type_traits>
41#include <utility>
42#include <valarray>
43#include <variant>
44#include <vector>
45
46#include "asserts.h"
47#include "traits.h"
48#include "transforms.h"
49
50// DO NOT USE
51//
52// Internal overloads of [CR_]EXPR.
54#define __EXPR1__(type, var, expr) [](type var) { return expr; }
55#define __EXPR2__(type, var1, var2, expr) \
56 [](type var1, type var2) { return expr; }
57#define __GET_MACRO__(_1, _2, _3, NAME, ...) NAME
59
71#define EXPR(...) \
72 __GET_MACRO__(__VA_ARGS__, __EXPR2__, __EXPR1__)(auto, __VA_ARGS__)
73
86#define CR_EXPR(...) \
87 __GET_MACRO__(__VA_ARGS__, __EXPR2__, __EXPR1__)(const auto &, __VA_ARGS__)
88
89namespace fcpp {
90
92namespace detail {
93
94// Seeding from std::random_device is expensive, so do it once per thread.
95inline std::mt19937 &random_engine() {
96 thread_local std::mt19937 engine(std::random_device{}());
97 return engine;
98}
99
100// Copies the item if possible, otherwise fails since it can't be duplicated.
101template <typename T>
102T copy_or_throw(const T &item, const char *operation) {
103 if constexpr (std::is_copy_constructible_v<T>) {
104 return item;
105 } else {
106 throw std::invalid_argument(
107 std::string(operation) + " requires copying a non-copyable item.");
108 }
109}
110
111} // namespace detail
113
114// Forward declaration. See actual definition below.
115template <typename T>
116class Queryable;
117
123template <typename T>
124class WhenTrue;
125
132template <typename WhenTrueQueried, typename T>
133class WhenFalse;
134
142template <typename TrueT, typename FalseT>
143class Merge;
144
152template <typename T>
153Queryable<T> query(std::vector<T> items);
154
189template <typename T>
190class Queryable final {
191public:
197 explicit Queryable(std::vector<T> items);
202 Queryable() = delete;
203 ~Queryable() = default;
209 Queryable(Queryable &&) noexcept = default;
215 Queryable(const Queryable &) = delete;
222 Queryable &operator=(const Queryable &) = delete;
229 operator std::vector<T>() && { return std::move(items_); }
235 operator std::vector<T>() const & { return items_; }
236
249 friend bool operator==(const Queryable<T> &lhs, const std::vector<T> &rhs) {
250 return lhs.items_ == rhs;
251 }
252
260 friend bool operator==(const Queryable<T> &lhs, const Queryable<T> &rhs) {
261 return lhs.items_ == rhs.items_;
262 }
263
267 typedef T item_type;
268
278 template <typename U, typename AccumulateFn>
279 U accumulate(U initial, AccumulateFn accumulate_func) const;
280
287 template <typename ActionFn>
288 Queryable<T> action(ActionFn action_func);
289
297 template <typename Predicate>
298 bool all(Predicate predicate) const;
299
307 template <typename Predicate>
308 bool any(Predicate predicate) const;
309
316 template <typename Predicate>
317 WhenTrue<T> branch(Predicate predicate);
318
329 Queryable<T> difference(std::vector<T> rhs_items);
330
337
344 [[nodiscard]] bool empty() const;
345
354 template <typename Predicate>
355 std::optional<T> first_or_default(Predicate predicate);
356
363 auto flatten();
364
372 template <typename KeySelector>
373 Queryable<std::vector<T>> group_by(KeySelector key_selector);
374
385 Queryable<T> intersect(std::vector<T> rhs_items);
386
403 template <typename U, typename LhsKeySelector, typename RhsKeySelector>
405 std::vector<U> rhs_items, LhsKeySelector lhs_key_selector,
406 RhsKeySelector rhs_key_selector);
407
416 template <typename KeySelector>
417 auto keyed_group_by(KeySelector key_selector);
418
424 T max();
425
433 template <typename ValueSelector>
434 T max(ValueSelector value_selector);
435
441 T min();
442
450 template <typename ValueSelector>
451 T min(ValueSelector value_selector);
452
464 template <typename ValueSelector>
465 Queryable<T> order_by(ValueSelector value_selector, bool descending = false);
466
473
481 template <typename Selector>
482 auto select(Selector selector);
483
490
496 [[nodiscard]] size_t size() const;
497
505 Queryable<T> skip(size_t value);
506
524 Queryable<T> slice(size_t start_index, size_t size, size_t stride = 1);
525
532
540 Queryable<T> take(size_t value);
541
550
558
566 template <typename KeySelector>
567 auto to_multi_value_map(KeySelector key_selector);
568
577 template <typename KeySelector /* = std::function<K(T)>*/>
578 auto to_single_value_map(KeySelector key_selector);
579
585 std::set<T> to_set();
586
592 std::vector<T> to_vector();
593
600 Queryable<T> unionize(std::vector<T> rhs_items);
601
609 template <typename Predicate>
610 Queryable<T> where(Predicate predicate);
611
624 template <typename U>
626 zip(std::vector<U> rhs_items, bool truncate = false);
627
640 template <typename U>
642 zip(std::initializer_list<U> rhs_items, bool truncate = false);
643
644private:
648 std::vector<T> items_;
649};
650
651} // namespace fcpp
652
653/******************************************************************************
654 *
655 * @brief Keep definitions separate to allow the declarations to stay clean and
656 * compact.
657 *
658 *****************************************************************************/
659
660namespace fcpp {
661
662template <typename T>
663Queryable<T> query(std::vector<T> items) {
664 return Queryable<T>(std::move(items));
665}
666
667template <typename T>
668Queryable<T>::Queryable(std::vector<T> items) : items_(std::move(items)) {}
669
670template <typename T>
671template <typename U, typename AccumulateFn>
672U Queryable<T>::accumulate(U initial, AccumulateFn accumulate_func) const {
673 // @todo Check that AccumulateFn is a binary operation.
674 // This method is const: do not attempt to move-from `items_` here. Use
675 // regular iterators so callers don't unexpectedly invalidate state.
676 return std::accumulate(
677 items_.begin(), items_.end(), initial, accumulate_func);
678}
679
680template <typename T>
681template <typename ActionFn>
682Queryable<T> Queryable<T>::action(ActionFn action_func) {
683 // @todo Check that ActionFn is a unary operation.
684 // Avoid copying each item into the lambda; forward as const-ref to reduce
685 // unnecessary copies for large types.
686 std::for_each(items_.begin(), items_.end(), [&action_func](const auto &item) {
687 action_func(item);
688 });
689 return Queryable<T>(std::move(items_));
690}
691
692template <typename T>
693template <typename Predicate>
694bool Queryable<T>::all(Predicate predicate) const {
695 return std::all_of(items_.begin(), items_.end(), predicate);
696}
697
698template <typename T>
699template <typename Predicate>
700bool Queryable<T>::any(Predicate predicate) const {
701 return std::any_of(items_.begin(), items_.end(), predicate);
702}
703
704template <typename T>
705Queryable<T> Queryable<T>::difference(std::vector<T> rhs_items) {
706 static_assert(
708 "T must be less-than comparable.");
709 std::sort(rhs_items.begin(), rhs_items.end());
710 // Number of matches consumed so far for each run of equivalent rhs items,
711 // indexed by the start of the run.
712 std::vector<size_t> consumed(rhs_items.size(), 0);
713 std::erase_if(items_, [&](const T &item) {
714 auto [lo, hi] = std::equal_range(rhs_items.begin(), rhs_items.end(), item);
715 size_t &used = consumed[lo - rhs_items.begin()];
716 if (lo + used < hi) {
717 used++;
718 return true;
719 }
720 return false;
721 });
722 return Queryable<T>(std::move(items_));
723}
724
725template <typename T>
727 static_assert(
729 "T must be less-than comparable.");
730 std::sort(items_.begin(), items_.end());
731 // Adjacent sorted items are equivalent when the first isn't less.
732 items_.erase(
733 std::unique(
734 items_.begin(), items_.end(),
735 [](const T &lhs, const T &rhs) { return !(lhs < rhs); }),
736 items_.end());
737 return Queryable<T>(std::move(items_));
738}
739
740template <typename T>
742 return items_.empty();
743}
744
745template <typename T>
746template <typename Predicate>
747std::optional<T> Queryable<T>::first_or_default(Predicate predicate) {
748 // @todo Require predicate to be a unary operation.
749 auto it =
750 std::find_if(items_.begin(), items_.end(), [&predicate](const T &item) {
751 return predicate(item);
752 });
753 if (it == items_.end()) {
754 return std::nullopt;
755 }
756 return std::optional<T>(std::move(*it));
757}
758
759template <typename T>
761 // @todo asserts::invariant T is a vector.
762 using U = typename T::value_type;
763 // Reserve capacity when possible: sum sizes of inner sequences to avoid
764 // repeated reallocations.
765 size_t total = 0;
766 for (T &item : items_) {
767 total += item.size();
768 }
769 std::vector<U> flattened;
770 flattened.reserve(total);
771 for (T &item : items_) {
772 for (U &sub_item : item) {
773 flattened.push_back(std::move(sub_item));
774 }
775 }
776 return Queryable<U>(std::move(flattened));
777}
778
779template <typename T>
780template <typename KeySelector>
782 using K = decltype(key_selector(*items_.begin()));
783 static_assert(
785 "Key selector must produce a value that is less-than comparable.");
786
787 std::map<K, std::vector<T>> keyed_groups;
788 for (T &item : items_) {
789 keyed_groups[key_selector(std::as_const(item))].push_back(std::move(item));
790 }
791
792 std::vector<std::vector<T>> groups;
793 groups.reserve(keyed_groups.size());
794 for (auto &[key, group] : keyed_groups) {
795 groups.push_back(std::move(group));
796 }
797 return Queryable<std::vector<T>>(std::move(groups));
798}
799
800template <typename T>
801Queryable<T> Queryable<T>::intersect(std::vector<T> rhs_items) {
802 static_assert(
804 "T must be less-than comparable.");
805 std::sort(rhs_items.begin(), rhs_items.end());
806 // Number of matches consumed so far for each run of equivalent rhs items,
807 // indexed by the start of the run.
808 std::vector<size_t> consumed(rhs_items.size(), 0);
809 std::erase_if(items_, [&](const T &item) {
810 auto [lo, hi] = std::equal_range(rhs_items.begin(), rhs_items.end(), item);
811 size_t &used = consumed[lo - rhs_items.begin()];
812 if (lo + used < hi) {
813 used++;
814 return false;
815 }
816 return true;
817 });
818 return Queryable<T>(std::move(items_));
819}
820
821template <typename T>
822template <typename U, typename LhsKeySelector, typename RhsKeySelector>
824 std::vector<U> rhs_items, LhsKeySelector lhs_key_selector,
825 RhsKeySelector rhs_key_selector) {
826 using KU = decltype(rhs_key_selector(*rhs_items.begin()));
827 using KT = decltype(lhs_key_selector(*items_.begin()));
828 static_assert(
829 std::is_same<KU, KT>::value,
830 "Left and right hand key selectors must produce the same type.");
831
832 using K = KT;
833 static_assert(
835 "Key selectors must produce a type that is less-than compareable.");
836
837 std::map<K, std::vector<U>> rhs_mapped;
838 for (U &rhs_item : rhs_items) {
839 rhs_mapped[rhs_key_selector(std::as_const(rhs_item))].push_back(
840 std::move(rhs_item));
841 }
842
843 // Count how many lhs items use each rhs group so the last use can move.
844 std::vector<K> lhs_keys;
845 lhs_keys.reserve(items_.size());
846 std::map<K, size_t> uses_left;
847 for (T &lhs_item : items_) {
848 lhs_keys.push_back(lhs_key_selector(std::as_const(lhs_item)));
849 if (rhs_mapped.count(lhs_keys.back())) {
850 uses_left[lhs_keys.back()]++;
851 }
852 }
853
854 std::vector<std::tuple<T, U>> joined;
855 for (size_t i = 0; i < items_.size(); i++) {
856 auto rhs_it = rhs_mapped.find(lhs_keys[i]);
857 if (rhs_it == rhs_mapped.end()) {
858 continue;
859 }
860 bool last_lhs_use = --uses_left[lhs_keys[i]] == 0;
861 std::vector<U> &rhs_group = rhs_it->second;
862 for (size_t j = 0; j < rhs_group.size(); j++) {
863 bool last_rhs_use = j + 1 == rhs_group.size();
864 joined.emplace_back(
865 last_rhs_use ? std::move(items_[i])
866 : detail::copy_or_throw(items_[i], "join"),
867 last_lhs_use ? std::move(rhs_group[j])
868 : detail::copy_or_throw(rhs_group[j], "join"));
869 }
870 }
871
872 return Queryable<std::tuple<T, U>>(std::move(joined));
873}
874
875template <typename T>
876template <typename KeySelector>
877auto Queryable<T>::keyed_group_by(KeySelector key_selector) {
878 using K = decltype(key_selector(*items_.begin()));
879
880 std::map<K, std::vector<T>> mapped;
881 for (T &item : items_) {
882 mapped[key_selector(std::as_const(item))].push_back(std::move(item));
883 }
885 {std::make_move_iterator(mapped.begin()),
886 std::make_move_iterator(mapped.end())});
887}
888
889template <typename T>
891 static_assert(
893 "T must be less-than compareable.");
894 asserts::invariant::eval(!items_.empty()) << "Sequence cannot be empty.";
895
896 // @todo Investigate performance for large objects.
897 return std::move(*std::max_element(items_.begin(), items_.end()));
898}
899
900template <typename T>
901template <typename ValueSelector>
902T Queryable<T>::max(ValueSelector value_selector) {
903 static_assert(
904 traits::is_less_than_comparable<decltype(value_selector(
905 *items_.begin()))>::value,
906 "ValueSelector return type must be less-than compareable.");
907 asserts::invariant::eval(!items_.empty()) << "Sequence cannot be empty.";
908 // @todo Require unary operation.
909
910 return std::move(*std::max_element(
911 items_.begin(), items_.end(),
912 [&value_selector](const auto &lhs, const auto &rhs) {
913 return value_selector(lhs) < value_selector(rhs);
914 }));
915}
916
917template <typename T>
919 static_assert(
921 "T must be less-than compareable.");
922 asserts::invariant::eval(!items_.empty()) << "Sequence cannot be empty.";
923
924 // @todo Guard with concepts as seen in min_element.
925 return std::move(*std::min_element(items_.begin(), items_.end()));
926}
927
928template <typename T>
929template <typename ValueSelector /* = std::function<K(T)>*/>
930T Queryable<T>::min(ValueSelector value_selector) {
931 static_assert(
932 traits::is_less_than_comparable<decltype(value_selector(
933 *items_.begin()))>::value,
934 "ValueSelector return type must be less-than compareable.");
935 asserts::invariant::eval(!items_.empty()) << "Sequence cannot be empty.";
936 // @todo Require unary operation.
937
938 return std::move(*std::min_element(
939 items_.begin(), items_.end(),
940 [&value_selector](const auto &lhs, const auto &rhs) {
941 return value_selector(lhs) < value_selector(rhs);
942 }));
943}
944
945template <typename T>
946template <typename ValueSelector>
948Queryable<T>::order_by(ValueSelector value_selector, bool descending) {
949 static_assert(
950 std::is_copy_assignable_v<T> ||
951 (std::is_move_assignable_v<T> && std::is_move_constructible_v<T>),
952 "T must either be copy assignable, or move assignable and "
953 "constructible.");
954 static_assert(
955 traits::is_less_than_comparable<decltype(value_selector(
956 *items_.begin()))>::value,
957 "ValueSelector return type must be less-than compareable.");
958
959 using K = std::decay_t<decltype(value_selector(*items_.begin()))>;
960 // Select each value once, then stable sort the indices by value.
961 std::vector<K> values;
962 values.reserve(items_.size());
963 for (T &item : items_) {
964 values.push_back(value_selector(std::as_const(item)));
965 }
966 std::vector<size_t> order(items_.size());
967 std::iota(order.begin(), order.end(), 0);
968 std::stable_sort(
969 order.begin(), order.end(),
970 [&values, descending](size_t lhs, size_t rhs) {
971 return descending ? values[rhs] < values[lhs]
972 : values[lhs] < values[rhs];
973 });
974
975 std::vector<T> ordered;
976 ordered.reserve(items_.size());
977 for (size_t i : order) {
978 ordered.push_back(std::move(items_[i]));
979 }
980 return Queryable<T>(std::move(ordered));
981}
982
983template <typename T>
985 std::reverse(items_.begin(), items_.end());
986 return Queryable<T>(std::move(items_));
987}
988
989template <typename T>
990template <typename Selector /* = std::function<U(T)>*/>
991auto Queryable<T>::select(Selector selector) {
992 using U = decltype(selector(*items_.begin()));
993 std::vector<U> selected;
994 selected.reserve(items_.size());
995 std::transform(
996 std::make_move_iterator(items_.begin()),
997 std::make_move_iterator(items_.end()), std::back_inserter(selected),
998 selector);
999
1000 return Queryable<U>(std::move(selected));
1001}
1002
1003template <typename T>
1005 std::shuffle(items_.begin(), items_.end(), detail::random_engine());
1006 return Queryable<T>(std::move(items_));
1007}
1008
1009template <typename T>
1010size_t Queryable<T>::size() const {
1011 return items_.size();
1012}
1013
1014template <typename T>
1016 asserts::invariant::eval(value <= size())
1017 << "Skip value " << value
1018 << " must be less than or equal to sequence size of " << size() << ".";
1019
1020 items_.erase(items_.begin(), items_.begin() + value);
1021 return Queryable<T>(std::move(items_));
1022}
1023
1024template <typename T>
1026Queryable<T>::slice(size_t start_index, size_t size, size_t stride) {
1027 if (size == 0) {
1028 return Queryable<T>(std::vector<T>());
1029 }
1030 asserts::invariant::eval(start_index < this->size())
1031 << "Slice start index " << start_index
1032 << " must be less than sequence size of " << this->size() << ".";
1033
1034 size_t last_index = start_index + (size - 1) * stride;
1035 asserts::invariant::eval(last_index < this->size())
1036 << "Slice last index " << last_index
1037 << " must be less than the sequence size of " << this->size() << ".";
1038
1039 std::vector<T> sliced;
1040 sliced.reserve(size);
1041 if (stride == 0) {
1042 // Every item is the same one, so all but the last must be copies.
1043 for (size_t i = 0; i + 1 < size; i++) {
1044 sliced.push_back(detail::copy_or_throw(items_[start_index], "slice"));
1045 }
1046 sliced.push_back(std::move(items_[start_index]));
1047 } else {
1048 for (size_t i = start_index; sliced.size() < size; i += stride) {
1049 sliced.push_back(std::move(items_[i]));
1050 }
1051 }
1052
1053 return Queryable<T>(std::move(sliced));
1054}
1055
1056template <typename T>
1058 std::sort(items_.begin(), items_.end());
1059 return Queryable<T>(std::move(items_));
1060}
1061
1062template <typename T>
1064 asserts::invariant::eval(value <= size())
1065 << "Take value " << value
1066 << " must be less than or equal to sequence size of " << size() << ".";
1067
1068 items_.erase(items_.begin() + value, items_.end());
1069 return Queryable<T>(std::move(items_));
1070}
1071
1072template <typename T>
1074 asserts::invariant::eval(value <= size())
1075 << "Take random value " << value
1076 << " must be less than or equal to sequence size of " << size() << ".";
1077
1078 // Partial Fisher-Yates shuffle: only the first value positions are drawn.
1079 auto &engine = detail::random_engine();
1080 for (size_t i = 0; i < value; i++) {
1081 std::uniform_int_distribution<size_t> dist(i, items_.size() - 1);
1082 std::swap(items_[i], items_[dist(engine)]);
1083 }
1084 items_.erase(items_.begin() + value, items_.end());
1085 return Queryable<T>(std::move(items_));
1086}
1087
1088template <typename T>
1089template <typename KeySelector /* = std::function<K(T)>*/>
1090auto Queryable<T>::to_multi_value_map(KeySelector key_selector) {
1091 using K = decltype(key_selector(*items_.begin()));
1092 std::map<K, std::vector<T>> mapped;
1093 for (T &item : items_) {
1094 mapped[key_selector(std::as_const(item))].push_back(std::move(item));
1095 }
1096 return mapped;
1097}
1098
1099template <typename T>
1100template <typename KeySelector /* = std::function<K(T)>*/>
1101auto Queryable<T>::to_single_value_map(KeySelector key_selector) {
1102 using K = decltype(key_selector(*items_.begin()));
1103 static_assert(
1105 "KeySelector return type must be less-than compareable.");
1106
1107 std::map<K, T> mapped;
1108 for (T &item : items_) {
1109 // Only moves the item if the key isn't already present.
1110 mapped.try_emplace(key_selector(std::as_const(item)), std::move(item));
1111 }
1112 return mapped;
1113}
1114
1115template <typename T>
1117 static_assert(
1119 "T must be less-than compareable.");
1120 return std::set<T>(
1121 std::make_move_iterator(items_.begin()),
1122 std::make_move_iterator(items_.end()));
1123}
1124
1125template <typename T>
1126std::vector<T> Queryable<T>::to_vector() {
1127 return std::move(items_);
1128}
1129
1130template <typename T>
1132 asserts::invariant::eval(size <= items_.size())
1133 << "Size " << size << " must be less than or equal to sequence size of "
1134 << this->size() << ".";
1135
1136 items_.erase(items_.end() - size, items_.end());
1137 return Queryable<T>(std::move(items_));
1138}
1139
1140template <typename T>
1141Queryable<T> Queryable<T>::unionize(std::vector<T> rhs_items) {
1142 static_assert(
1144 "T must be less-than compareable.");
1145
1146 items_.insert(
1147 items_.end(), std::make_move_iterator(rhs_items.begin()),
1148 std::make_move_iterator(rhs_items.end()));
1149 return distinct();
1150}
1151
1152template <typename T>
1153template <typename Predicate>
1154Queryable<T> Queryable<T>::where(Predicate predicate) {
1155 // Filter in place; the predicate sees each item before any are moved.
1156 std::erase_if(
1157 items_, [&predicate](const T &item) { return !predicate(item); });
1158 return Queryable<T>(std::move(items_));
1159}
1160
1161template <typename T>
1162template <typename U>
1164Queryable<T>::zip(std::vector<U> rhs_items, bool truncate) {
1165 size_t common = std::min(items_.size(), rhs_items.size());
1166 size_t total = truncate ? common : std::max(items_.size(), rhs_items.size());
1167
1168 std::vector<std::tuple<T, U>> zipped;
1169 zipped.reserve(total);
1170 for (size_t i = 0; i < common; i++) {
1171 zipped.emplace_back(std::move(items_[i]), std::move(rhs_items[i]));
1172 }
1173 if (total == common) {
1174 return Queryable<std::tuple<T, U>>(std::move(zipped));
1175 }
1176
1177 // Pad the shorter side with default values.
1178 if (items_.size() > rhs_items.size()) {
1179 if constexpr (std::is_default_constructible_v<U>) {
1180 for (size_t i = common; i < total; i++) {
1181 zipped.emplace_back(std::move(items_[i]), U());
1182 }
1183 } else {
1184 throw std::invalid_argument(
1185 "zip padding requires a default constructible right hand type.");
1186 }
1187 } else {
1188 if constexpr (std::is_default_constructible_v<T>) {
1189 for (size_t i = common; i < total; i++) {
1190 zipped.emplace_back(T(), std::move(rhs_items[i]));
1191 }
1192 } else {
1193 throw std::invalid_argument(
1194 "zip padding requires a default constructible left hand type.");
1195 }
1196 }
1197 return Queryable<std::tuple<T, U>>(std::move(zipped));
1198}
1199
1200template <typename T>
1201template <typename U>
1203Queryable<T>::zip(std::initializer_list<U> rhs_items, bool truncate) {
1204 return zip(std::vector<U>(std::move(rhs_items)), truncate);
1205}
1206
1214template <typename TrueT, typename FalseT>
1215class Merge {
1216public:
1226 explicit Merge(Queryable<TrueT> true_queried, Queryable<FalseT> false_queried)
1227 : true_queried_(std::move(true_queried)),
1228 false_queried_(std::move(false_queried)) {}
1229 Merge() = delete;
1230 ~Merge() = default;
1231 Merge(const Merge &) = delete;
1232 Merge &operator=(const Merge &) = delete;
1233
1243 return true_queried_.zip(false_queried_.to_vector(), truncate);
1244 }
1245
1246private:
1247 Queryable<TrueT> true_queried_;
1248 Queryable<FalseT> false_queried_;
1249};
1250
1257template <typename WhenTrueQueriedT, typename T>
1259public:
1270 explicit WhenFalse(
1271 Queryable<WhenTrueQueriedT> when_true_queried,
1272 std::vector<T> when_false_items)
1273 : when_true_queried_(std::move(when_true_queried)),
1274 when_false_items_(std::move(when_false_items)) {}
1275 WhenFalse() = delete;
1276 ~WhenFalse() = default;
1277 WhenFalse(const WhenFalse &) = delete;
1278 WhenFalse &operator=(const WhenFalse &) = delete;
1279
1290 template <
1291 typename WhenFalseQuery /* =
1292 std::function<Queryable<WhenFalseQueriedT>(Queryable<T>&)>
1293 */
1294 ,
1295 typename WhenFalseQueriedT = typename std::invoke_result_t<
1296 WhenFalseQuery, Queryable<T>>::item_type>
1298 when_false(WhenFalseQuery when_false_query) {
1300 std::move(when_true_queried_),
1301 when_false_query(Queryable<T>(std::move(when_false_items_))));
1302 }
1303
1304private:
1305 Queryable<WhenTrueQueriedT> when_true_queried_;
1306 std::vector<T> when_false_items_;
1307};
1308
1314template <typename T>
1316public:
1326 explicit WhenTrue(
1327 std::vector<T> when_true_items, std::vector<T> when_false_items)
1328 : when_true_items_(std::move(when_true_items)),
1329 when_false_items_(std::move(when_false_items)) {}
1330 WhenTrue() = delete;
1331 ~WhenTrue() = default;
1332 WhenTrue(const WhenTrue &) = delete;
1333 WhenTrue &operator=(const WhenTrue &) = delete;
1334
1345 template <
1346 typename WhenTrueQuery /* =
1347 std::function<Queryable<WhenTrueQueriedT>(Queryable<T>)>
1348 */
1349 ,
1350 typename WhenTrueQueriedT =
1351 typename std::invoke_result_t<WhenTrueQuery, Queryable<T>>::item_type>
1352 WhenFalse<WhenTrueQueriedT, T> when_true(WhenTrueQuery when_true_query) {
1354 when_true_query(Queryable<T>(std::move(when_true_items_))),
1355 std::move(when_false_items_));
1356 }
1357
1358private:
1359 std::vector<T> when_true_items_;
1360 std::vector<T> when_false_items_;
1361};
1362
1363template <typename T>
1364template <typename Predicate>
1365WhenTrue<T> Queryable<T>::branch(Predicate predicate) {
1366 std::vector<T> when_true_items;
1367 std::vector<T> when_false_items;
1368 for (T &item : items_) {
1369 if (predicate(std::as_const(item))) {
1370 when_true_items.push_back(std::move(item));
1371 } else {
1372 when_false_items.push_back(std::move(item));
1373 }
1374 }
1375
1376 return WhenTrue<T>(std::move(when_true_items), std::move(when_false_items));
1377}
1378
1379} // namespace fcpp
1380
1381#endif // FCPP_QUERY_H
Runtime validation of object states.
Merge result of both if and else block queries from Queryable<T>::branch method.
Definition query.h:1215
Merge(Queryable< TrueT > true_queried, Queryable< FalseT > false_queried)
Construct a new Merge object from the transformed true and false branches of the predicate.
Definition query.h:1226
Queryable< std::tuple< TrueT, FalseT > > merge(bool truncate=false)
Merges the true and false branch sequences as a zip.
Definition query.h:1242
Core object used to query items and hold the sequence state.
Definition query.h:190
auto to_single_value_map(KeySelector key_selector)
Groups items by selected key value, takes the first item of every group, and puts them into a hash ma...
Definition query.h:1101
std::set< T > to_set()
Gets the sequence as a set.
Definition query.h:1116
bool all(Predicate predicate) const
Determines whether all items of a sequence satisfy a condition.
Definition query.h:694
Queryable< T > shuffle()
Randomizes / shuffles all the item's order in the sequence.
Definition query.h:1004
bool any(Predicate predicate) const
Determines whether a sequence contains any items.
Definition query.h:700
Queryable< std::tuple< T, U > > join(std::vector< U > rhs_items, LhsKeySelector lhs_key_selector, RhsKeySelector rhs_key_selector)
Correlates the items of two sequences based on matching keys.
Definition query.h:823
auto flatten()
Projects each item in the sequence-of-sequences and flattens the resulting sequences into one (i....
Definition query.h:760
T item_type
Type of items to query over. Used for type deduction.
Definition query.h:267
Queryable< T > where(Predicate predicate)
Selects items in the sequence that satisfy the predicate / conditional.
Definition query.h:1154
Queryable< T > skip(size_t value)
Bypasses a specified number of items in the sequence and then returns the remaining items.
Definition query.h:1015
Queryable< std::tuple< T, U > > zip(std::vector< U > rhs_items, bool truncate=false)
Produces a sequence of tuples with items from the two specified sequences.
Definition query.h:1164
Queryable< T > sort()
Sorts the items in the sequence.
Definition query.h:1057
friend bool operator==(const Queryable< T > &lhs, const std::vector< T > &rhs)
Equals overload operator for Queryable<T> against a std::vector<T>.
Definition query.h:249
Queryable< T > unionize(std::vector< T > rhs_items)
Unions the left hand and right hand side sequences.
Definition query.h:1141
Queryable< T > order_by(ValueSelector value_selector, bool descending=false)
Orders the sequence by the selected value.
Definition query.h:948
Queryable< T > reverse()
Inverts the order of the items in the sequence.
Definition query.h:984
T min()
Gets the minimum item from the sequence.
Definition query.h:918
std::optional< T > first_or_default(Predicate predicate)
Gets the first item of a sequence, or a default value if no item is found.
Definition query.h:747
auto to_multi_value_map(KeySelector key_selector)
Groups items by a selected key value and puts them into a hash map.
Definition query.h:1090
Queryable< T > difference(std::vector< T > rhs_items)
Produces the multiset difference of two sequences.
Definition query.h:705
Queryable< T > take_random(size_t value)
Takes the specified number of items from the sequence at random places.
Definition query.h:1073
Queryable(Queryable &&) noexcept=default
Move assignment operator.
Queryable< T > distinct()
Gets distinct items from a sequence.
Definition query.h:726
Queryable< T > take(size_t value)
Takes a specified number of contiguous items from the start of a sequence.
Definition query.h:1063
std::vector< T > to_vector()
Gets the sequence as a vector.
Definition query.h:1126
Queryable< T > slice(size_t start_index, size_t size, size_t stride=1)
Gets a slice of the sequence.
Definition query.h:1026
Queryable()=delete
Empty constructor.
size_t size() const
Gets the size of the sequence.
Definition query.h:1010
bool empty() const
Indicates of the sequence is empty.
Definition query.h:741
Queryable(std::vector< T > items)
Construct a new Queryable object from a sequence of items.
Definition query.h:668
U accumulate(U initial, AccumulateFn accumulate_func) const
Sums all the projected values of the sequence into a single value.
Definition query.h:672
Queryable< T > intersect(std::vector< T > rhs_items)
Produces the multiset intersection of two sequences.
Definition query.h:801
T max()
Gets the maximum item from the sequence.
Definition query.h:890
auto select(Selector selector)
Projects each item of a sequence into a new form.
Definition query.h:991
friend bool operator==(const Queryable< T > &lhs, const Queryable< T > &rhs)
Equals overload operator.
Definition query.h:260
Queryable< T > trim(size_t size)
Trim from the back of the sequence.
Definition query.h:1131
Queryable< std::vector< T > > group_by(KeySelector key_selector)
Groups the items of a sequence.
Definition query.h:781
Queryable< T > action(ActionFn action_func)
Executes a void action to create side effects.
Definition query.h:682
WhenTrue< T > branch(Predicate predicate)
Branches the sequence into two based on a condition.
Definition query.h:1365
auto keyed_group_by(KeySelector key_selector)
Groups the items of a sequence by key and produces as a key-group pair sequence.
Definition query.h:877
False / else block query of Queryable<T>::branch method.
Definition query.h:1258
Merge< WhenTrueQueriedT, WhenFalseQueriedT > when_false(WhenFalseQuery when_false_query)
Projects each item of the false branched sequence into a new form from the Queryable<T>::branch of th...
Definition query.h:1298
WhenFalse(Queryable< WhenTrueQueriedT > when_true_queried, std::vector< T > when_false_items)
Construct a new WhereFalse object from a sequence of transformed items from the true branch of the pr...
Definition query.h:1270
True / if block query of Queryable<T>::branch method.
Definition query.h:1315
WhenTrue(std::vector< T > when_true_items, std::vector< T > when_false_items)
Construct a new WhereTrue object from a sequence of true and false branched items of the predicate th...
Definition query.h:1326
WhenFalse< WhenTrueQueriedT, T > when_true(WhenTrueQuery when_true_query)
Projects each item of the true branched sequence into a new form from the Queryable<T>::branch of the...
Definition query.h:1352
static invariant eval(bool condition)
Evaluate the invariant and throw std::invalid_argument if violated.
Definition asserts.h:100
Queryable< T > query(std::vector< T > items)
Queries the sequence of items using a vector.
Definition query.h:663
Traits for asserting object features.
Transforms between standard container types.