C ++ comparing a set of values ​​with a given value

I need to compare one given value with the resulting values. I do this several times in code. I am not satisfied with how this looks, and I am looking for some utility function. Has anyone written?

The number of values ​​I'm comparing with is known at compile time.

Update: I would like to get rid of containers, because I know the exact number of values ​​(often no more than 3) that I want to compare with. And it is not always convenient to place objects in a container every time.
I do not like if , because it is not obvious how to "find."

#include <algorithm> #include <string> #include <vector> std::string getValue1() { return "test"; } std::string getValue2() { return "the"; } std::string getValue3() { return "world"; } int main() { const std::string value = "the"; // simple if if ( value == getValue1() || value == getValue2() || value == getValue3() ) return 1; // using collections like vector, set std::vector<std::string> values; values.push_back( getValue1() ); values.push_back( getValue2() ); values.push_back( getValue3() ); if ( values.end() != std::find( values.begin(), values.end(), value ) ) return 1; // third option I'd use instead // return 0; } 
+3
source share
10 answers

For your request

 if (InSet(value)(GetValue1(), GetValue2(), GetValue3())) { // Do something here... } 

Try the following:

 template <typename T> class InSetHelper { const T &Value; void operator=(const InSetHelper &); public: InSetHelper(const T &value) : Value(value) {} template<class Other, class Another> bool operator()(const Other &value1, const Another &value2) const { return Value == value1 || Value == value2; } template<class Other, class Another, class AThird> bool operator()(const Other &value1, const Another &value2, const AThird &value3) const { return Value == value1 || Value == value2 || Value == value3; } }; template <typename T> InSetHelper<T> InSet(const T &value) { return InSetHelper<T>(value); } 

This syntax may be more clear:

 if (MakeSet(GetValue1(), GetValue2(), GetValue3()).Contains(value)) { // Do something here... } template <typename T, typename U, typename V> class Set3 { const T& V1; const U& V2; const V& V3; void operator=(const Set3 &); public: Set3(const T &v1, const U &v2, const V &v3) : V1(v1), V2(v2), V3(v3) {} template <typename W> bool Contains(const W &v) const { return V1 == v || V2 == v || V3 == v; } }; template <typename T, typename U> class Set2 { // as above }; template <typename T, typename U, typename V> Set3<T, U, V> MakeSet(const T &v1, const U &v2, const V &v3) { return Set3<T, U, V>(v1, v2, v3); } template <typename T, typename U> Set3<T, U> MakeSet(const T &v1, const U &v23) { return Set3<T, U, V>(v1, v2); } 

If these values ​​are indeed part of a tree or linked list, you already have your own set / container, and it is best to use some recursion:

 parent.ThisOrDescendantHasValue(value); 

You would just add this to what class the parent and child belong to:

 class Node { public: Value GetValue(); Node *GetChild(); bool ThisOrDescendantHasValue(const Value &value) { return GetValue() == value || (GetChild() && GetChild->ThisOrDescendantHasValue(value)); } }; 
+1
source

If the values ​​you are looking for are comparable to the <operator (for example, ints, float and std :: strings), then it is faster to use std :: set to place the values ​​there, and then check set.find (value) == set.end (). This is due to the fact that the set will store values ​​in a certain order, which allows faster searches. Using a hash table will be even faster. However, for less than 50 values ​​or so, you may not notice any difference :) Thus, my rule of thumb is:

  • Less than 5 elements: if with multiple ||

  • 5 or more: enter into a table or hash table

+7
source

you can write a set of template functions to help you with this, for example:

 template <typename T> bool InSet(const T & item, const T & i1, const T & i2) { return item==i1 || item==i2; } template <typename T> bool InSet(const T & item, const T & i1, const T & i2, const T & i3) { return item==i1 || item==i2 || item==i3; } 

Note that you can make InSet work as if it took a variable number of arguments by creating multiple templates with different number of arguments.

And then:

 int i; if (InSet(i, 3, 4, 5)) { ... } string s; if (InSet(s, "foobar", "zap", "garblex")) { ... } 

and etc.

+3
source

You don't need std :: set or std :: vector. Just use std :: set_intersection () ...

The code is better ...

 #include <set> #include <iostream> #include <iterator> using namespace std; #define COUNT(TYPE,ARRAY) ( sizeof(ARRAY) / sizeof(TYPE) ) inline bool CaseInsensitiveCompare (const char * a, const char * b) { return strcasecmp( a, b ) < 0; } int main() { const char * setA[] = { "the", "world", "is", "flat" }; const char * setB[] = { "the", "empty", "set", "is", "boring" }; stable_sort( setA, setA + COUNT( const char *, setA ), CaseInsensitiveCompare ); stable_sort( setB, setB + COUNT( const char *, setB ), CaseInsensitiveCompare ); cout << "Intersection of sets: "; set_intersection( setA, setA + COUNT( const char *, setA ), setB, setB + COUNT( const char *, setB ), ostream_iterator<const char *>(cout, " "), CaseInsensitiveCompare ); cout << endl << endl; } 

Or maybe, given your 1-N search problem:
(Note: use binary_search () AFTER collation!)

 if ( binary_search( setA, setA + COUNT( const char *, setA ), "is", CaseInsensitiveCompare ) ) ... if ( binary_search( setA, setA + COUNT( const char *, setA ), "set", CaseInsensitiveCompare ) ) ... 
+1
source

Which will change more, the "value" or the values ​​returned by "getValueX ()"? You can insert everything into the hash_map / map, and then search in the way you suggested with the containers.

+1
source

std::find_first_of .

Of course, if you only care about finding a single value, you can create your own wrapper around std::find .

0
source

I would recommend your method 2 with std :: vector or another container and search for membership. Since the order of the elements you are checking is probably irrelevant, you can use std :: set or std :: map. If you have a lot of elements in your set of values, then using a set or map will be faster, and if you have only a few vectors, they may be faster.

The advantage of any of these approaches is that you can then keep the set / map somewhere generally accepted and avoid the need to create a set of relevant answers each time.

0
source

I like the approach to collections, maybe use hash_set instead of a vector. store values ​​in the properties file and have a method for populat hash_set from the file, and another to return a boolean if the value is in hash_set. then you can go to one line in the main code.

0
source

It depends on the source of the extracted values, if you are reading from a file or stream, then you are doing something else, but if your source is a series of functions, then this is another way to do this, not perfect, but can satisfy your needs:

 const int count = 3; std::string value = "world"; boost::function<std::string(void)> funcArray[count]; funcArray[0] = &getValue1; funcArray[1] = &getValue2; funcArray[2] = &getValue3; for( int i = 0; i < count; ++i ) { if( funcArray[i]() == value ) return 1; } 

If you know which functions are the source (as well as the number of objects), I expect that you can compile an array of function pointers with a preprocessor.

0
source

How about using boost :: array (or std :: tr1 :: array) and creating a simple function like this:

 template <typename ValueType, size_t arraySize> bool contains(const boost::array<ValueType, arraySize>& arr, const ValueType& val) { return std::find(arr.begin(), arr.end(), val)!=arr.end(); } 

You could reuse this quite easily:

 #include <string> #include <iostream> #include <boost\array.hpp> template <typename ValueType, size_t arraySize> bool contains(const boost::array<ValueType, arraySize>& arr, const ValueType& val) { return std::find(arr.begin(), arr.end(), val)!=arr.end(); } int _tmain(int argc, _TCHAR* argv[]) { boost::array<std::string, 3> arr = {"HI", "there", "world"}; std::cout << std::boolalpha << "arr contains HI: " << contains(arr, std::string("HI")) << std::endl << "arr contains blag: " << contains(arr, std::string("blag") ) << std::endl << "arr contains there: " << contains(arr, std::string("there") ) << std::endl; return 0; } 

Edit:. Thus, forcing is disabled. This is pretty easy to adapt to a regular array:

 template <typename ValueType, size_t arraySize> bool contains(ValueType (&arr)[arraySize], const ValueType& val) { return std::find(&arr[0], &arr[arraySize], val)!=&arr[arraySize]; } int _tmain(int argc, _TCHAR* argv[]) { std::string arr[3] = {"HI", "there", "world"}; std::cout << std::boolalpha << "arr contains HI: " << contains(arr, std::string("HI")) << std::endl << "arr contains blag: " << contains(arr, std::string("blag") ) << std::endl << "arr contains there: " << contains(arr, std::string("there") ) << std::endl; return 0; } 
0
source

All Articles