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;
}
Community
  • 1
  • 1
user3784804
  • 35
  • 1
  • 1
  • 8
  • 3
    Change: `int FindValue(vector& vec, string keyWord)` by `int FindValue(vector& 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`. – NetVipeC Sep 05 '14 at 18:51

3 Answers3

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
 }
codenheim
  • 20,467
  • 1
  • 59
  • 80
  • 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. – user3784804 Sep 05 '14 at 19:11
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;
}
Tim Ludwinski
  • 2,704
  • 30
  • 34
  • Very nice solution. Thanks. Are there any drawbacks to using set and map over vector? – user3784804 Sep 05 '14 at 19:12
  • 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. – Tim Ludwinski Oct 05 '14 at 21:11
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);
}
GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52