Access a nested ROS-Message - c++

I would like to access the second element of ROS-Message message_a.
# ROS-Message message_a
Header header
message_b[] test
ROS-Message 1 contains ROS-Message 2!
# ROS-Message message_b
Header header
uint32[] result
In my main code I loop with a for-each loop through the message test with the datatyp message_a.
for ( message_a::Test1 test : message_a.message_b ) {
uint32_t c = test.result;
}
How can I access for example the second element of message_b? I need that because I want to get the result of the second test.
With the for-each loop that you see above, it will loop through all elements of message_b. How can I change this for-each loop to a general for-loop? Then I could just loop from 2 to 3...

You can change the range-based (aka for-each) loop to an index based for loop like this which iterates over the second, third, ..., and final result of test 42:
std::size_t test_idx = 42; // Second element of results
std::size_t result_start = 1; // start from the second result
std::size_t result_end = your_msg_obj.test.at(test_idx).size(); // run until end of all in the given test
// For loop that iterates over all results in a given test
for ( std::size_t result_idx = result_start; idx < result_end; ++idx ) {
uint32_t c = your_msg_obj.test.at(test_idx).result.at(result_idx);
// ... fancy stuff
}

The ROS documentatation of messages explains that array message fields are generated as std::vector in C++. In your case the field test is of type std::vector<message_b>, result of type std::vector<uint32_t>.
C++ vectors can be accessed in serval ways, you can find them in in this answer. In your case simply accessing the items by index should be possible like:
for(size_t i = 0; i != your_message_a.test.size(); i++)
{
//Access the second result item of each test item
uint32_t c = your_message_a.test[i].result[1];
}

Related

Why I could not assign value and display element of list?

int Square(int lenght)
{
std::vector < int > square(lenght);
for(int counter :square)
square[counter] = counter * counter ;
for(int counter : square){
printf("%d",square[counter]);
cout<<""<<endl;
}
Hi there
As you see I have basis code block in cpp . I try to make Range-loop using for loop always taking "0" from terminal awkward.How can I fill out with square values ? What is the problem technically or What would you advice for me at similiar case?
for(int counter :square)
Here you are using range based for loop. What this means is that in every step counter will go through elements instead of indices. If you want to use indices you should instead use a standard for loop:
for (int counter = 0; counter < length; ++counter)
You can't use the range loop like that. The range loop loops through every element in the vector and assigns the left variable in the loop the value of the element.
E.g. your output code should look like this:
for(auto const& val: square){
std::cout<<val<<std::endl;
}

How to get int position of vector loop

How to get int position of this loop? Thank you.
auto a = vect.begin();
auto b = vect2.begin();
auto c = vect3.begin();
for (; a != vect.end() && b != vect2.end() && c != vect3.end(); a++, b++, c++) {
}
I need to print values of other variable, but I need to get actual unsigned int position of this vector loop.
I need to print double vector using this position of this vector.
And how to get the last index of vector.
My problem is for for loop with multiple vectors and getting index from it next to use only last of indexes.
As Angew shows, a simple indexed loop may be preferable when you need indices.
However, it is possible to get the index from an iterator as well:
auto a = vect.begin();
auto b = vect2.begin();
auto c = vect3.begin();
for (/*the loop conditions*/) {
auto index = a - vect.begin();
}
It is also possible to get the index of a forward iterator using std::distance, but it would be unwise to use it in a loop, since the complexity will be linear for non-random-access iterators.
In the case of forward iterators (and generic code that must support forward iterators), you can write a loop which has both the index variable, and the iterators.
P.S. it is potentially preferable to use pre-increment with iterators. Probably only matters in debug build.
It's simple: if you need indices, don't use iterators:
for (
size_t idx = 0, idxEnd = std::min({vect.size(), vect2.size(), vect3.size()});
idx < idxEnd;
++idx
)
{
auto& obj1 = vect[idx];
auto& obj2 = vect2[idx];
auto& obj3 = vect3[idx];
}
(The above code initialises idxEnd once at the start of the loop, so that it's not needlessly recomputed at each iteration. It's just an optimisation).

Start value for vector loop

Say I have the following:
int numFields = 0;
for ( auto & isFieldBlank : InputProcessor::numericFields_isBlank ) {
if ( !isFieldBlank ) {
numFields += 1;
}
}
InputProcessor::numericFields_isBlank is a bool vector of all numeric input values indicating whether the input values are empty true or populated false.
I have two related questions:
Is there a better way to count the populated fields in the vector?
Is there a way to provide a starting index to the for loop iterator?
A range based for loop will always run the entire range, you cant change that unless you adapt the range. What you can do though is use std::count/std::count_if to count the instances for you like
auto count = std::count(container.begin(), container.end(), true);
and to change the start and stop positions you can use std::next to move the iterator like
auto count = std::count(std::next(container.begin(), some_value),
std::next(container.end(), -some_other_value),
true);
Is there a better way to count the populated fields in the vector?
You can simplify the body of the for loop to:
numFields += (!isFieldBlank);
The complete for loop will be
for ( auto & isFieldBlank : InputProcessor::numericFields_isBlank ) {
numFields += (!isFieldBlank);
}
Is there a way to provide a starting index to the for loop iterator?
You certainly can. However, you will need to use a normal for loop, not a range-for loop.

2D Vector - Remove Rows by search

I'm quite new to vector and need some additional help with regards to vector manipulation.
I've currently created a global StringArray Vector that is populated by string values from a text file.
typedef std::vector<std::string> StringArray;
std::vector<StringArray> array1;
I've created a function called "Remove" which takes the input from the user and will eventually compare the input against the first value in the array to see whether it's a match. If it is, the entire row will then deleted and all elements beneath the deleted row will be "shuffled up" a position to fill the game.
The populated array looks like this:
Test1 Test2 Test3
Cat1 Cat2 Cat3
Dog1 Dog2 Dog3
And the remove function looks like this:
void remove()
{
string input;
cout << "Enter the search criteria";
cin >> input;
I know that I will need a loop to iterate through the array and compare each element with the input value and check whether it's a match.
I think this will look like:
for (int i = 0; i < array1.size(); i++)
{
for (int j = 0; j < array1[i].size(); j++)
{
if (array1[i] = input)
**//Remove row code goes here**
}
}
But that's as far as I understand. I'm not really sure A) if that loop is correct and B) how I would go about deleting the entire row (not just the element found). Would I need to copy across the array1 to a temp vector, missing out the specified row, and then copying back across to the array1?
I ultimately want the user to input "Cat1" for example, and then my array1 to end up being:
Test1 Test2 Test3
Dog1 Dog2 Dog3
All help is appreciated. Thank you.
So your loop is almost there. You're correct in using one index i to loop through the outer vector and then using another index j to loop through the inner vectors. You need to use j in order to get a string to compare to the input. Also, you need to use == inside your if statement for comparison.
for (int i = 0; i < array1.size(); i++)
{
for (int j = 0; j < array1[i].size(); j++)
{
if (array1[i][j] == input)
**//Remove row code goes here**
}
}
Then, removing a row is the same as removing any vector element, i.e. calling array1.erase(array1.begin() + i); (see How do I erase an element from std::vector<> by index?)
Use std::list<StringArray> array1;
Erasing an item from an std::vector is less efficient as it has to move all the proceeding data.
The list object will allow you to remove an item (a row) from the list without needing to move the remaining rows up. It is a linked list, so it won't allow random access using a [ ] operator.
You can use explicit loops, but you can also use already implemented loops available in the standard library.
void removeTarget(std::vector<StringArray>& data,
const std::string& target) {
data.erase(
std::remove_if(data.begin(), data.end(),
[&](const StringArray& x) {
return std::find(x.begin(), x.end(), target) != x.end();
}),
data.end());
}
std::find implements a loop to search for an element in a sequence (what you need to see if there is a match) and std::remove_if implements a loop to "filter out" elements that match a specific rule.
Before C++11 standard algorithms were basically unusable because there was no easy way to specify custom code parameters (e.g. comparison functions) and you had to code them separately in the exact form needed by the algorithm.
With C++11 lambdas however now algorithms are more usable and you're not forced to create (and give a reasonable name to) an extra global class just to implement a custom rule of matching.

c++ - Tricky Method - need solution

The array of objects tArray contains buyer names and the numshares of there purchases, each buyer can be in the array of objects more than once. I have to return in an array the names of the five largest buyers.
I attempted to run two arrays in parallel with the buyer name and there total volume in another array.
my method in general flawed as i am getting wrong results, how can I solve this problem.
Thanks
ntransactions = the number of transactions in the array
string* Analyser::topFiveBuyers()
{
//set size and add buyer names for comparison.
const int sSize = 5;
string *calcString = new string[sSize];
calcString[0] = tArray[0].buyerName;
calcString[1] = tArray[1].buyerName;
calcString[2] = tArray[2].buyerName;
calcString[3] = tArray[3].buyerName;
calcString[4] = tArray[4].buyerName;
int calcTotal[sSize] = {INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN};
//checks transactions
for (int i = 0; i<nTransactions; i++)
{
//compares with arrays
for(int j =0; j<sSize; j++)
{
//checks if the same buyer and then increase his total
if(tArray[i].buyerName == calcString[j])
{
calcTotal[j] += tArray[i].numShares;
break;
}
//checks if shares is great then current total then replaces
if(tArray[i].numShares > calcTotal[j])
{
calcTotal[j] = tArray[i].numShares;
calcString[j] = tArray[i].buyerName;
break;
}
}
}
return calcString;
}
Assuming you're allowed to, I'd start by accumulating the values into an std::map:
std::map<std::string, int> totals;
for (int i=0; i<ntransactions; i++)
totals[tarray[i].buyername] += tarray[i].numshares;
This will add up the total number of shares for each buyer. Then you want to copy that data to an std::vector, and get the top 5 by number of shares. For the moment, I'm going to assume your struct (with buyername and numshares as members) is named transaction.
std::vector<transaction> top5;
std::copy(totals.begin(), totals.end(), std::back_inserter(top5));
std::nth_element(top5.begin(), top5.begin()+5, top5.end(), by_shares());
For this to work, you'll need a comparison functor named by_shares that looks something like:
struct by_shares {
bool operator()(transaction const &a, transaction const &b) {
return b.numshares < a.numshares;
}
};
Or, if you're using a compiler new enough to support it, you could use a lambda instead of an explicit functor for the comparison:
std::nth_element(totals.begin(), totals.end()-5, totals.end(),
[](transaction const &a, transaction const &b) {
return b.numshares < a.numshares;
});
Either way, after nth_element completes, your top 5 will be in the first 5 elements of the vector. I've reversed the normal comparison to do this, so it's basically working in descending order. Alternatively, you could use ascending order, but specify the spot 5 from the end of the collection instead of 5 from the beginning.
I should add that there are other ways to do this -- for example, a Boost bimap would do the job pretty nicely as well. Given that this sounds like homework, my guess is that a pre-packaged solution like bimap that handles virtually the entire job for you probably would't/won't be allowed (and even std::map may be prohibited for pretty much the same reason).
As you can have several times the same buyer, you must store a counter for all buyers, not only for 5 of them as there is no way to know that a buyer you remove from the top 5 should not be part of this top 5 (as more items could be linked to this buyer later in tArray).
I would suggest to use a stl map with key being buyer name and value the number of items. You fill it by iterating on tArray and sum all items bought by the same buyer.
Then you can iterate on the map and retrieve the 5 top buyers easily as you have only one entry per buyer.
When the outer loop start, the index i is zero, and the same for the inner loop. This means that the first condition checks tArray[0].buyerName == calcString[0] which is equal as you set it that way before the loops. This leads to calcTotal[0] is increased from -2147483648 and leaving the inner loop.
I'm not certain, but this doesn't seem like something one would want.