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;
}
CodePudding user response:
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;
}
CodePudding user response:
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];});
}
CodePudding user response:
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))