I made the following program where there is a struct
struct data
{
int integers; //input of integers
int times; //number of times of appearance
}
and there is a vector of this struct
std::vector<data> inputs;
and then I'll get from a file an integer of current_int
std::fstream openFile("input.txt")
int current_int; //current_int is what I want to check if it's in my vector of struct (particularly in inputs.integers)
openFile >> current_int;
and I wanna check if current_int is already stored in my vector inputs.
I've tried researching about finding data in a vector and supposedly you use an iterator like this:
it = std::find(inputs.begin(),inputs.end(),current_int)
but will this work if it's in a struct? Please help.
There are two variants of find:
find() searches for a plain value. In you case you have a vector of data, so the values passed to find() should be data.
find_if() takes a predicate, and returns the first position where the predicates returns true.
Using the latter, you can easily match one field of your struct:
auto it = std::find_if(inputs.begin(), inputs.end(),
[current_int] (const data& d) {
return d.integers == current_int;
});
Note that the above uses a C++11 lambda function. Doing this in earlier versions of C++ requires you to create a functor instead.
Related
I need to find the most frequent element in an array of custom structs. There is no custom ID to them just matching properties.
I was thinking of sorting my vector by frequency but I have no clue how to do that.
I'm assuming by frequency you mean the number of times an identical structure appears in the array.
You probably want to make a hash function (or overload std::hash<> for your type) for your custom struct. Then iterate over your array, incrementing the value on an unordered_map<mytype, int> for every struct in the array. This will give you the frequency in the value field. Something like the below would work:
std::array<mytype> elements;
std::unordered_map<mytype, int> freq;
mytype most_frequent;
int max_frequency = 0;
for (const mytype &el : elements) {
freq[el]++;
if (freq[el] > max_frequency) {
most_frequent = el;
}
}
For this to work, the map will need to be able to create a hash for the above function. By default, it tries to use std::hash<>. You are expressly allowed by the standard to specialize this template in the standard namespace for your own types. You could do this as follows:
struct mytype {
std::string name;
double value;
};
namespace std {
template <> struct hash<mytype> {
size_t operator()(const mytype &t) const noexcept {
// Use standard library hash implementations of member variable types
return hash<string>()(t.name) ^ hash<double>()(t.value)
}
}
}
The primary goal is to ensure that any two variables that do not contain exactly the same values will generate a different hash. The above XORs the results of the standard library's hash function for each type together, which according to Mark Nelson is probably as good as the individual hashing algorithms XOR'd together. An alternative algorithm suggested by cppreference's hash reference is the Fowler-Noll-Vo hash function.
Look at std::sort and the example provided in the ref, where you actually pass your own comparator to do the trick you want (in your case, use the frequencies). Of course, a lambda function can be used too, if you wish.
I have a map of std::map<int,float,float> m_mapWheelvalue; of 100 elements
I need to read the values .Code i am using is below:
float fvalue1,fvalue2;
std::map<double,float,float>::iterator itNewMap;
itNewMap= m_mapWheelvalue.find(20);
if(itNewMap!= m_mapWheelvalue.end())
{
fValue1 = itNewMap->second;
fValue2= itNewMap->third;
}
but its giving error !!{ third not defined }
How to read third value
Please give proper solution
The following compiled for me:
#include <map>
int main(){
std::map<double,std::pair<float,float> > m_mapWheelvalue;
float fValue1,fValue2;
std::map<double,std::pair<float,float> >::iterator itNewMap;
itNewMap= m_mapWheelvalue.find(20);
if(itNewMap!= m_mapWheelvalue.end()){
fValue1 = itNewMap->second.first;
fValue2= itNewMap->second.second;
}
}
Notes:
Look at std::map definition: first parameter is the key, second parameter is the entry... third parameter is the comparison function. I guess you wanted to have several values in the entry. I've chosen to use a pair (as you have two), if you have more you might want to define a struct/class.
Check variable names, there are several case changes.
The iterator to a map gets you a pair of key,entry... so itNewMap->first is the key, itNewMap->second is the entry.
I am trying to use std::string as a key in the stxxl::map
The insertion was fine for small number of strings about 10-100.
But while trying to insert large number of strings about 100000 in it, I am getting segmentation fault.
The code is as follows:
struct CompareGreaterString {
bool operator () (const std::string& a, const std::string& b) const {
return a > b;
}
static std::string max_value() {
return "";
}
};
// template parameter <KeyType, DataType, CompareType, RawNodeSize, RawLeafSize, PDAllocStrategy (optional)>
typedef stxxl::map<std::string, unsigned int, CompareGreaterString, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> name_map;
name_map strMap((name_map::node_block_type::raw_size)*3, (name_map::leaf_block_type::raw_size)*3);
for (unsigned int i = 0; i < 1000000; i++) { /// Inserting 1 million strings
std::stringstream strStream;
strStream << (i);
Console::println("Inserting: " + strStream.str());
strMap[strStream.str()]=i;
}
In here I am unable to identify why I am unable to insert more number of strings. I am getting segmentation fault exactly while inserting "1377". Plus I am able to add any number of integers as key. I feel that the variable size of string might be causing this trouble.
Also I am unable to understand what to return for max_value of the string. I simply returned a blank string.
According to documentation:
CompareType must also provide a static max_value method, that returns a value of type KeyType that is larger than any key stored in map
Because empty string happens to compare as smaller than any other string, it breaks this precondition and may thus cause unspecified behaviour.
Here's a max_value that should work. MAX_KEY_LEN is just an integer which is larger or equal to the length of the longest possible string key that the map can have.
struct CompareGreaterString {
// ...
static std::string max_value() {
return std::string(MAX_KEY_LEN, std::numeric_limits<unsigned char>::max());
}
};
I have finally found the solution to my problem with great help from Timo bingmann, user2079303 and Martin Ba. Thank you.
I would like to share it with you.
Firstly stxxl supports POD only. That means it stores fixed sized structures only. Hence std::string cannot be a key. stxxl::map worked for about 100-1000 strings because they were contained in the physical memory itself. When more strings are inserted it has to write on disk which is internally causing some problems.
Hence we need to use a fixed string using char[] as follows:
static const int MAX_KEY_LEN = 16;
class FixedString {
public:
char charStr[MAX_KEY_LEN];
bool operator< (const FixedString& fixedString) const {
return std::lexicographical_compare(charStr, charStr+MAX_KEY_LEN,
fixedString.charStr, fixedString.charStr+MAX_KEY_LEN);
}
bool operator==(const FixedString& fixedString) const {
return std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
}
bool operator!=(const FixedString& fixedString) const {
return !std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
}
};
struct comp_type : public std::less<FixedString> {
static FixedString max_value()
{
FixedString s;
std::fill(s.charStr, s.charStr+MAX_KEY_LEN, 0x7f);
return s;
}
};
Please note that all the operators mainly((), ==, !=) need to be overriden for all the stxxl::map functions to work
Now we may define fixed_name_map for map as follows:
typedef stxxl::map<FixedString, unsigned int, comp_type, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> fixed_name_map;
fixed_name_map myFixedMap((fixed_name_map::node_block_type::raw_size)*5, (fixed_name_map::leaf_block_type::raw_size)*5);
Now the program is compiling fine and is accepting about 10^8 strings without any problem.
also we can use myFixedMap like std::map itself. {for ex: myFixedMap[fixedString] = 10}
If you are using C++11, then as an alternative to the FixedString class you could use std::array<char, MAX_KEY_LEN>. It is an STL layer on top of an ordinary fixed-size C array, implementing comparisons and iterators as you are used to from std::string, but it's a POD type, so STXXL should support it.
Alternatively, you can use serialization_sort in TPIE. It can sort elements of type std::pair<std::string, unsigned int> just fine, so if all you need is to insert everything in bulk and then access it in bulk, this will be sufficient for your case (and probably faster depending on the exact case).
I am trying to find the difference in my code when I use std::find.
For my test code. I made a Vector called Test
std::vector<const char*> Test;
To test the find function, I filled the Test vector with dummy data by using push_back function
Test.push_back("F_S");
Test.push_back("FC");
Test.push_back("ID");
Test.push_back("CD");
Test.push_back("CT");
Test.push_back("DS");
Test.push_back("CR");
Test.push_back("5K_2");
Test.push_back("10K_5");
Test.push_back("10K_1");
Test.push_back("10K_2");
Test.push_back("10K_3");
Test.push_back("10K_4");
Test.push_back("10K_5");
What I want to do with the find function is to go through the Test and see if there are any repeated data. The first time a encounter the data, I will save it to a vector called Unique_Data.
std::vector<const char*> Unique_Data;
So for the 14 data points above, only 13 will be saved because 10K_5 repeated.
The Code I am using looks like this
for(int i = 0; i < Test.size(); i++)
{
if( Unique_Data.empty())
{
Unique_Data.push_back(Test[i]);
}
else if (std::find(Unique_Data.begin(), Unique_Data.end(), Test[i]) != Unique_Data.end())
{
// Move on to next index
}
else
{
Unique_Data.push_back(Test[i]);
}
}
The problem I am having is when I am using the dummy data. I am getting a correct answer for Unique_Data.
However, if I save the actual data into the Test vector which are saved in linked list. I get that they are all unique.
The code looks like this
p_curr = List.p_root;
while(p_curr != NULL)
{
// id starts from 0
if(atoi(p_curr->id) == 14) break;
Test.push_back(p_curr->Descriptor);
p_curr = p_curr->p_next;
}
I tested with the same 14 data. They are all const char* types. However, when I used the linked list data. The find function thinks all the data is unique.
Can anyone tell me what is wrong with this?
Using C-style strings is a bit tricky, they are just a pointer, and pointers are compared by identity. Two C strings with the same sequence of characters, but different addresses will compare different.
const char first[] = "Hi";
const char second[] = "Hi";
assert(first == second); // will fail!
There are two solutions to this problem. The simple one is using std::string in your container, as std::string will provide value comparisons. The alternative is to pass a comparison functor to std::find as a last argument. But this will still leave the problem of managing the lifetime of the const char*-s stored in the vector.
This is a pointers problem. You're not storing strings in your array, you're storing the memory address of the data in the string.
This strange behaviour is probably because in your example case you have literal strings that cannot be changed, so the compiler is optimising the storage, and when two strings are the same then it stores the same address for all strings that have the same text.
In your real data example, you have a bunch of strings that hold the same data, but each of these strings lives at a different memory address, so the find function is saying that all strings have a different address.
In summary, your find function is looking at the memory address of the string, not the data (text) in the string. If you use std::strings then this problem will disappear.
I would highly recommend using strings, as performance is going to be more than good enough and they eliminate a vast number of problems.
As David Rodriguez mentions in his answer, you're only comparing pointers, and not the contents of the strings themselves. Your solution will work as is if you were storing std::strings instead of char const *. With the latter, you need to resort to std::find_if and a predicate that calls strcmp to determine whether the strings are identical.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
int main()
{
std::vector<const char*> Test;
Test.push_back("F_S");
Test.push_back("FC");
Test.push_back("ID");
Test.push_back("CD");
Test.push_back("CT");
Test.push_back("DS");
Test.push_back("CR");
Test.push_back("5K_2");
Test.push_back("10K_5");
Test.push_back("10K_1");
Test.push_back("10K_2");
Test.push_back("10K_3");
Test.push_back("10K_4");
Test.push_back("10K_5");
std::vector<const char*> Unique_Data;
for(auto const& s1 : Test) {
if(std::find_i(Unique_Data.cbegin(), Unique_Data.cend(),
[&](const char *s2) { return std::strcmp(s1, s2) == 0; })
== Unique_Data.cend()) {
Unique_Data.push_back(s1);
}
}
for(auto const& s : Unique_Data) {
std::cout << s << '\n';
}
}
Here's a live example
I have a vector, in which I save objects. I need to convert it to set. I have been reading about sets, but I still have a couple of questions:
How to correctly initialize it? Honestly, some tutorials say it is fine to initialize it like set<ObjectName> something. Others say that you need an iterator there too, like set<Iterator, ObjectName> something.
How to insert them correctly. Again, is it enough to just write something.insert(object) and that's all?
How to get a specific object (for example, an object which has a named variable in it, which is equal to "ben") from the set?
I have to convert the vector itself to be a set (a.k.a. I have to use a set rather than a vector).
Suppose you have a vector of strings, to convert it to a set you can:
std::vector<std::string> v;
std::set<std::string> s(v.begin(), v.end());
For other types, you must have operator< defined.
All of the answers so far have copied a vector to a set. Since you asked to 'convert' a vector to a set, I'll show a more optimized method which moves each element into a set instead of copying each element:
std::vector<T> v = /*...*/;
std::set<T> s(std::make_move_iterator(v.begin()),
std::make_move_iterator(v.end()));
Note, you need C++11 support for this.
You can initialize a set using the objects in a vector in the following manner:
vector<T> a;
... some stuff ...
set<T> s(a.begin(), a.end());
This is the easy part. Now, you have to realize that in order to have elements stored in a set, you need to have bool operator<(const T&a, const T& b) operator overloaded. Also in a set you can have no more then one element with a given value acording to the operator definition. So in the set s you can not have two elements for which neither operator<(a,b) nor operator<(b,a) is true. As long as you know and realize that you should be good to go.
If all you want to do is store the elements you already have in a vector, in a set:
std::vector<int> vec;
// fill the vector
std::set<int> myset(vec.begin(), vec.end());
You haven't told us much about your objects, but suppose you have a class like this:
class Thing
{
public:
int n;
double x;
string name;
};
You want to put some Things into a set, so you try this:
Thing A;
set<Thing> S;
S.insert(A);
This fails, because sets are sorted, and there's no way to sort Things, because there's no way to compare two of them. You must provide either an operator<:
class Thing
{
public:
int n;
double x;
string name;
bool operator<(const Thing &Other) const;
};
bool Thing::operator<(const Thing &Other) const
{
return(Other.n<n);
}
...
set<Thing> S;
or a comparison function object:
class Thing
{
public:
int n;
double x;
string name;
};
struct ltThing
{
bool operator()(const Thing &T1, const Thing &T2) const
{
return(T1.x < T2.x);
}
};
...
set<Thing, ltThing> S;
To find the Thing whose name is "ben", you can iterate over the set, but it would really help if you told us more specifically what you want to do.
How to correctly initialize it?
std::set<YourType> set;
The only condition is that YourType must have bool operator<(const YourType&) const and by copyable (default constructor + assignment operator). For std::vector copyable is enough.
How to insert them correctly.
set.insert(my_elem);
How to get specific object (for example object, which has name variable in it, which is equal to "ben") from set?
That's maybe the point. A set is just a bunch of object, if you can just check that an object is inside or iterate throught the whole set.
Creating a set is just like creating a vector. Where you have
std::vector<int> my_vec;
(or some other type rather than int) replace it with
std::set<int> my_set;
To add elements to the set, use insert:
my_set.insert(3);
my_set.insert(2);
my_set.insert(1);