Reputation: 89
I have a std::vector<bool>
of size N and a std::vector<std::size_t>
of variable size containing indexes in [0, N)
. What is an idiomatic way to check if the first vector is true at all indexes given by the second vector?
My possibly naive solution is:
auto all_true(
std::vector<bool> const& bools, std::vector<std::size_t> const& indexes)
-> bool {
auto res = true;
for (auto index : indexes) {
res = res and bools[index];
}
return res;
}
Upvotes: 2
Views: 384
Reputation: 51815
An idiomatic (though not necessarily efficient) way to do this would be to use the std::all_of
STL function, using a predicate that simply returns the value of the Boolean vector at the index specified by each value in the size_t
vector.
Here's an outline/demo:
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
bool all_true(const std::vector<bool>& data, const std::vector<size_t>& test)
{
return std::all_of(test.begin(), test.end(), [&data](size_t n) { return data[n]; });
}
int main()
{
std::vector<bool> bools{ true, true, true, true, false, false, true, false, true, false };
std::vector<size_t> test1{ 0, 1, 2, 3, 6, 8 }; // all true
std::vector<size_t> test2{ 0, 1, 2, 4, 7, 9 }; // some false
std::cout << "Test1: " << all_true(bools, test1) << "\n";
std::cout << "Test2: " << all_true(bools, test2) << "\n\n";
// Just to show that the order doesn't matter ...
std::cout << "After shuffling ...\n";
std::random_device rdev;
std::mt19937 rgen(rdev());
std::shuffle(test1.begin(), test1.end(), rgen);
std::shuffle(test2.begin(), test2.end(), rgen);
std::cout << "Test1: " << all_true(bools, test1) << "\n";
std::cout << "Test2: " << all_true(bools, test2) << "\n";
return 0;
}
Upvotes: 3
Reputation: 2778
Yet another option would be std::accumulate
. If the result type and the input std::vector
element type were the same, you could instead use a (more efficient) std::reduce
. However, in this case you need to (sort of) accumulate size_t
s (indices pointing into a std::vector
of bool
s) into a bool
result:
#include <iostream>
#include <numeric>
#include <vector>
int main() {
const std::vector<bool> bools{true, true, false, true, true};
const std::vector<size_t> i1{1, 4, 3, 0}; // true && true && true && true
const std::vector<size_t> i2{0, 2, 4, 3}; // true && false && true && true
const auto and_by_idx{
[&bools](bool acc, size_t idx) { return acc && bools[idx]; }};
std::cout << std::boolalpha
<< std::accumulate(i1.begin(), i1.end(), true, and_by_idx) << '\n'
<< std::accumulate(i2.begin(), i2.end(), true, and_by_idx) << '\n';
}
I wouldn’t call any of the C++ solutions idiomatic though. Some other languages have shorter and more elegant ways to express this, like Python and its all()
.
bools = (True, True, False, True, True)
i1 = (1, 4, 3, 0) # True && True && True && True
i2 = (0, 2, 4, 3) # True && False && True && True
print(all(bools[i] for i in i1))
print(all(bools[i] for i in i2))
Upvotes: 1
Reputation: 264331
Don't think your code is not idiomatic.
The range based for()
is there to handle this type of situation. Especially if you are writing a named function to encapsulate it.
auto allTrue(std::vector<bool> const& data, std::vector<std::size_t> const& indexs) -> bool
{
for (auto index: indexs) {
if (!data[index]) {
return false;
}
}
return true;
}
Pre range based iterator I would say use a standard function that uses iterators.
auto allTrue(std::vector<bool> const& data, std::vector<std::size_t> const& indexs) -> bool
{
return std::all_of(std::begin(indexs), std::end(indexs),
[&data](std::size_t i){return data[i];}
);
}
Maybe: If you are not going to wrap it in a function then using this std::all_of()
function directly in place in the code it makes it more intuitive to read, then use it. But if you are writing the function anyway use the range based for.
Or if you have C++20
auto allTrue(std::vector<bool> const& data, std::vector<std::size_t> const& indexs) -> bool
{
return std::ranges::all_of(indexs, [&data](std::size_t i){return data[i];});
}
Upvotes: 2