0

Question: Is there a better way to loop through all the vectors in the structure than by calling them one by one as shown in the example? #

I am building a classifier. There are several categories of items each represented by a vector of string. Each of the vectors will be fairly small, < 100 elements. I have read the examples in the following link:

How to find if an item is present in a std::vector?

Here is a simplified example of the code that I am implementing. The code compiles and runs but seems clunky to me. Thanks in advance.

#include "stdafx.h"
#include <vector>
#include <string>
#include <iostream>

using namespace std;

struct Categories
{
    vector <string> cars;
    vector <string> food;
};

struct ThingsType
{
    Categories iLike;
    Categories dontLike;
};

typedef vector<string>::const_iterator vIter;

int FindValue(vector<string>& vec, string keyWord)
{
    int indx = -1;
    vIter iter = find(vec.begin(), vec.end(), keyWord);
    if (iter != vec.end()){
        // found it 
        std::cout << "Value:  " << *iter << " found in location:  " << iter - vec.begin() << endl;
        indx = iter - vec.begin();
    }
    else
    {
        // did not find it.
        std::cout << "Value:  " << keyWord << " not found." << endl;
    }
    return indx;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int result[10];
    ThingsType things;
    things.iLike.cars = { "Mustang", "Pinto" };
    things.dontLike.food = { "Squash" };
    string item("Pinto");
    // I want to loop over all vectors searching for items
    result[0] = FindValue(things.iLike.cars, item);
    result[1] = FindValue(things.iLike.food, item);
    // . . .
    result[9] = FindValue(things.dontLike.food, item);

    return 0;
}
1
  • 3
    Change: int FindValue(vector<string>& vec, string keyWord) by int FindValue(vector<string>& vec, const string& keyWord), you are copying the string every time that call FindValue. Other possible improvement is sort the array of categories and find with lower_bound. Commented Sep 5, 2014 at 18:51

3 Answers 3

1

You can keep the struct, and store pointers to each struct member in a list or vector.

struct Categories
{
    vector <string> cars;
    vector <string> food;
};

std::vector<std::vector<string>*> members;
Categories cat;
members.push_back(&cat.cars);  // add a pointer to the cars struct member
members.push_back(&cat.food);  // add a pointer to the food struct member

Now you can iterate via members, or access via the struct.

for(std::vector<string>* member: members)
{
    for(string s: *member)
    {

    } 
} 

Or you could just forego the structure altogether and use the vector of vectors, that way you can iterate the "members" while still allowing O(1) access time for each member.

 const int CARS = 0;
 const int FOOD = 1; 
 members[CARS].push_back(car);   // add a car
 members[FOOD].push_back(food);  // add food

 // iterate cars
 for(string car: members[CARS])
 {
    // do something
 }
Sign up to request clarification or add additional context in comments.

1 Comment

I like the vector of vectors. Combine that with enum { CARS, FOOD } and now I can access the vectors with members[CARS]. I sure like python where I can iterate over things like enum.
1

Try using a std::map

using namespace std;

typedef set<string> CategoryType;

struct ThingsType
{
    map<string, CategoryType> iLike;
    map<string, CategoryType> dontLike;
};

int _tmain(int argc, _TCHAR* argv[])
{
    ThingsType things;
    things.iLike['cars'].insert("Mustang");
    things.iLike['cars'].insert("Pinto");
    things.iLike['food'].insert("Squash");

    string item("Pinto");
    // I want to loop over all vectors searching for items
    for(map<string, CategoryType>::const_iterator i = things.iLike.begin(); i != things.iLike.end(); i++) {
        if (i->second.find(item) != set::end)
            cout << "I like " << item << endl;
    }

    for(map<string, CategoryType>::const_iterator i = things.dontLike.begin(); i != things.dontLike.end(); i++) {
        if (i->second.find(item) != set::end)
            cout << "I don't like " << item << endl;
    }

    return 0;
}

2 Comments

Very nice solution. Thanks. Are there any drawbacks to using set and map over vector?
With sets and maps you can find a value in O(ln n) time as opposed to O(n) time with a vector. As far as disadvantages go, there is no ordering of items in most set and map types and you will be using a negligibly greater amount of memory for each item.
0

I think std::unordered_set would be better in this case.

#include<unordered_set>

struct Categories{
    std::unordered_set <std::string> cars;
    std::unordered_set <std::string> food;
};

struct ThingsType{
    Categories iLike;
    Categories dontLike;
};

int main(){
    std::unordered_set<std::string>::iterator result[10];
    ThingsType things;
    things.iLike.cars = { "Mustang", "Pinto" };
    things.dontLike.food = { "Squash" };
    string item("Pinto");
    result[0] = things.iLike.cars(item);
    result[1] = things.iLike.food(item);
    // . . .
    result[9] = things.dontLike.food(item);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.