std::map sequencing of operator[] and size() - c++

I have a std::map and trying to fill it with pairs (name, id). The id field is simply generated from map's size(). Here's a simplified version:
#include <iostream>
#include <map>
struct A {
std::string name;
int id;
A(const std::string &s) : name(s), id(-1) { }
};
class Database {
std::map<std::string, int> ids;
public:
void insert(A *item) {
ids[item->name] = item->id = ids.size();
}
void dump() const {
for (std::map<std::string, int>::const_iterator i = ids.begin(); i != ids.end(); i++)
std::cout << i->second << ". " << i->first << std::endl;
}
};
int main(int argc, char **agrv) {
A a("Test");
Database db;
db.insert(&a);
db.dump();
return 0;
}
The problem is that different compilers treat the ids[item->name] = item->id = ids.size() part differently. Clang++ produces
item->id = ids.size(); // First item gets 0
ids[item->name] = item->id;
when g++ does something like
ids.insert(std::pair<std::string, int>(item->name, 0));
item->id = ids.size(); // First item gets 1
ids[item->name] = item->id;
So, is this code valid (from the STL perspective) or it is as evil as i = ++i + ++i?

ids[item->name] = item->id = ids.size();
Without a sequence point separating the two calls, the compiler is free to evaluate operator[] and size() in any order it likes. There is no guarantee that size() will be called before operator[].

change ids[item->name] = item->id = ids.size(); to
item->id = ids.size();
ids[item->name] = item->id;

Related

How to create a large number of combinations lazily in C++

I want to create a combination of K elements one each from K sets. Each set can have n elements in it.
set1 = {a1, a2, a3}
set2 = {b1, b2, b3 , b4}
set3 = {c1, c2}
Required Combinations = {{a1,b1,c1}, {a1,b2,c1} ... {a3,b4,c2}}
Number of combinations = 3*4*2 =24
So if K is large and n is large we run into Out of Memory very quickly. Refer to the below code snippet how we are creating combinations today. If we create all the combinations in a case where K is relatively large, we go out of memory! So for instance, if K=20 and each set has 5 elements, the combinations are 5^20, which is extremely large in memory. So I want an alternative algorithm where I don't need to store all those combinations in memory all at a time before I start consuming the combinations.
vector<vector<string>> setsToCombine;
vector<vector<string>> allCombinations;
vector<vector<string>> *current =
new vector<vector<string>>{vector<string>()};
vector<vector<string>> *next = new vector<vector<string>>();
vector<vector<string>> *temp;
for (const auto& oneSet : setsToCombine) {
for (auto& cur : *current) {
for (auto& oneEle : oneSet) {
cur.push_back(oneEle);
next->push_back(cur);
cur.pop_back();
}
}
temp = current;
current = next;
next = temp;
next->clear();
}
for (const auto& cur : *current) {
allCombinations.push_back(cur);
}
current->clear();
next->clear();
delete current;
delete next;
You can store the indexes and lazely iterate over the combinations
#include <cstdint>
#include <iostream>
#include <vector>
using v_size_type = std::vector<int>::size_type;
using vv_size_type = std::vector<v_size_type>::size_type;
bool increment(std::vector<v_size_type> &counters, std::vector<v_size_type> &ranges) {
for (auto idx = counters.size(); idx > 0; --idx) {
++counters[idx - 1];
if (counters[idx - 1] == ranges[idx - 1]) counters[idx - 1] = 0;
else return true;
}
return false;
}
std::vector<int> get(const std::vector<std::vector<int>> &sets, const std::vector<v_size_type> &counters) {
std::vector<int> result(sets.size());
for (vv_size_type idx = 0; idx < counters.size(); ++idx) {
result[idx] = sets[idx][counters[idx]];
}
return result;
}
void print(const std::vector<int> &result) {
for (const auto el : result) {
std::cout << el << ' ';
}
}
int main() {
const std::vector<std::vector<int>> sets = {{-5, 2}, {-100, -21, 0, 15, 32}, {1, 2, 3}};
std::vector<v_size_type> ranges(sets.size());
for (vv_size_type idx = 0; idx < sets.size(); ++idx) {
ranges[idx] = sets[idx].size();
}
std::vector<v_size_type> counters(sets.size());
while (true) {
print(get(sets, counters));
std::cout << '\n';
if (!increment(counters, ranges)) break;
}
}
Godbolt
You can also use the odometer approach.
First, let us look again, what an odometer is. It looks like this:
There are several disks, with values printed on it. And if the odometer runs forward, it will show the Cartesian product of all values on the disks.
That is somehow clear, but how to use this principle? The solution is, that each set of values will be a disk, and the values of the set, will be put on the corresponding disk. With that, we will have an odometer, where the number of values on each disk is different. But this does not matter.
Also here, if a disks overflows, the next disk is incremented. Same principle like a standard odometer. Just with maybe more or less values.
And, you can put everything on a disk, not just integers. This approach will work always.
We can abstract a disk as a std::vector of your desired type. And the odometer is a std::vector of disks.
All this we can design in a class. And if we add iterator functionality to the class, we can easily handle it.
In the example below, I show only a minimum set of functions. You can add as many useful functions to this class as you like and tailor it to your needs.
The object oriented approach is often better to understand in the end.
Please check:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <initializer_list>
#include <algorithm>
#include <iterator>
using MyType = int;
using Disk = std::vector<MyType>;
using Disks = std::vector<Disk>;
// Abstraction for a very simple odometer
class Odometer {
Disks disks{};
public:
// We will do nearly everything with the iterator of the odometer class
struct iterator {
// Definitions for iterator ----------------
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::vector<MyType>;
using pointer = std::vector<MyType>*;
using reference = std::vector<MyType>&;
const Disks& d; // Reference to disks from super class
int overflow{}; // Indicates an overflow of all disks
std::vector<std::size_t>positions{}; // Stores position of any disks
// Iterator constructor
iterator(const Disks& dd, const int over = 0) : d(dd), overflow(over) {
positions = std::vector<std::size_t>(dd.size(), 0);
}
// Dereference iterator
value_type operator*() const {
std::vector<MyType> result(d.size());
for (std::size_t i{}; i < d.size(); ++i) result[i] = d[i][positions[i]];
return result;
};
// Comparison
bool operator != (const iterator& other) { return positions != other.positions or overflow != other.overflow; }
// And increment the iterator
iterator operator++() {
int carry = 0; std::size_t i{};
for (i=0; i < d.size(); ++i) {
if (positions[i] >= d[i].size() - 1) {
positions[i] = 0;
carry = 1;
}
else {
++positions[i];
carry = 0;
break;
}
}
overflow = (i == d.size() and carry) ? 1 : 0;
return *this;
}
};
// Begin and End functions. End is true, if there is a flip over of all disks
iterator begin() const { return iterator(disks); }
iterator end() const { return iterator(disks, 1); }
// Constructors
Odometer() {}; // Default (useless for this example)
// Construct from 2d initializer list
Odometer(const std::initializer_list<const std::initializer_list<MyType>> iil) {
for (const std::initializer_list<MyType>& il : iil) {
disks.push_back(il);
}
}
// Variadic. Parameter pack and fold expression
template <typename ... Args>
Odometer(Args&&... args) {
(disks.push_back(std::forward<Args>(args)), ...);
}
// Simple output of everything
friend std::ostream& operator << (std::ostream& os, const Odometer& o) {
for (const auto vi : o) {
for (const MyType i : vi) os << i << ' ';
os << '\n';
}
return os;
}
};
// Some test
int main() {
// Define Odometer. Initialiaze wit normal initializer list
Odometer odo1{ {1,2},{3},{4,5,6} };
// Show complete output
std::cout << odo1 << "\n\n\n";
// Create additional 3 vectors for building a new cartesian product
std::vector<MyType> v1{ 1,2 };
std::vector<MyType> v2{ 3,4 };
std::vector<MyType> v3{ 5,6 };
// Define next Odometer and initialize with variadic constructor
Odometer odo2(v1, v2, v3);
// Use range based for loop for output
for (const std::vector<MyType>& vm : odo2) {
for (const MyType i : vm) std::cout << i << ' ';
std::cout << '\n';
}
}

How do I make a std::map not need to compare entire strings twice?

std::map makes comparisons by checking if the value being searched for is less than the value currently being compared against, so this means if the map keys are strings, it will need to compare the entire strings twice if they're equal.
I tried to get around this by recording the last strings compared, and using the result of the last comparison if the strings are the same. The problem is that I'm pretty sure this isn't thread safe, so I think I would need to use a lock every time I used the map.
Is there a better way to do this?
#include <iostream>
#include <chrono>
#include <string>
#include <map>
const char* lastComparedlhs = nullptr;
const char* lastComparedrhs = nullptr;
int lastCompResult = 0;
struct ComparerForMap
{
bool operator()(const std::string& lhs, const std::string& rhs) const
{
if (rhs.data() == lastComparedlhs && lhs.data() == lastComparedrhs)
{
lastComparedlhs = nullptr;
lastComparedrhs = nullptr;
return lastCompResult != 0;
}
lastComparedlhs = lhs.data();
lastComparedrhs = rhs.data();
lastCompResult = lhs.compare(rhs);
return lastCompResult < 0;
}
};
int main()
{
std::map<std::string, int> normalMap;
std::map<std::string, int, ComparerForMap> specialMap;
std::string str1(10000000, 'a');
std::string str2(str1);
normalMap[str1] = 123;
specialMap[str1] = 123;
auto start1 = std::chrono::high_resolution_clock::now();
int n1 = normalMap[str2];
auto stop1 = std::chrono::high_resolution_clock::now();
auto duration1 = std::chrono::duration_cast<std::chrono::nanoseconds>(stop1 - start1);
auto start2 = std::chrono::high_resolution_clock::now();
int n2 = specialMap[str2];
auto stop2 = std::chrono::high_resolution_clock::now();
auto duration2 = std::chrono::duration_cast<std::chrono::nanoseconds>(stop2 - start2);
std::cout << "normalMap: " << duration1.count() << '\n';
std::cout << "specialMap: " << duration2.count() << "\npress enter to exit\n";
// normalMap: about 4000000 ns
// specialMap: about 2000000 ns
char ch = getchar();
}

C++ List showing last 3 items from table

Hey i have a table of teams with the names and the points they have and i'm trying to figure out how to display the last 3 teams with the least amount of points in the table?
It displays all the teams and i want it to display only the last 3 in the table but don't know what way to go about it.
These are my Accessors
string GetName
int GetPoints
int lowest = 1000;
for (int i = 0; i < numTeams; i++)
{
if (league[i].GetPoints() < lowest)
{
lowest = league[i].GetPoints();
}
}
for (int i = 0; i < numTeams; i++)
{
if (league[i].GetPoints() == lowest)
{
cout << "\tThe lowest goals against is: " << league[i].GetName() << endl;
}
}
Actually, you don't need variable lowest, if you would sort the data before printing.
#include <algorithm>
// Sort using a Lambda expression.
std::sort(std::begin(league), std::end(league), [](const League &a, const League &b) {
return a.GetPoints() < b.GetPoints();
});
int last = 3;
for (int i = 0; i < last; i++)
{
cout << "\tThe lowest goals against is: " << league[i].GetName() << endl;
}
U could probably start by sorting your array
#include <algorithm>
std::array<int> foo;
std::sort(foo.begin(), foo.end());
and then Iterate From Your Last Element to your Last - 3. (U can use Reverse Iterators)
for (std::vector<int>::reverse_iterator it = v.rend() ; it != v.rend() + 3;
it++) {
//Do something
}
or by using auto
for (auto it = v.rend() ; it != v.rend() + 3; ++it) {
//Do something
}
In my example I've created test class(TestTeam) to implement several important methods for objects in your task.
I use std::sort method to sort container of objects, by default std::sort compares objects by less(<) operation, so I have overrided operator < for TestTeam object
bool operator < ( const TestTeam& r) const
{
return GetPoints() < r.GetPoints();
}
Also we could pass as third parameter another compare method or lambda method as shown in below answers:
std::sort(VecTeam.begin(), VecTeam.end(), [](const TestTeam& l, const TestTeam& r)
{
return l.GetPoints() < r.GetPoints();
});
And example when we use global method to compare:
bool CompareTestTeamLess(const TestTeam& l, const TestTeam& r)
{
return l.GetPoints() < r.GetPoints();
}
//...
// some code
//...
// In main() we use global method to sort
std::sort(VecTeam.begin(), VecTeam.end(), ::CompareTestTeamLess);
You can try my code with vector as container:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
// Test class for example
class TestTeam
{
public:
TestTeam(int16_t p, const std::string& name = "Empty name"):mPoints(p), mName(name)
{
};
int16_t GetPoints() const {return mPoints;}
const std::string& GetName() const {return mName;}
void SetName( const std::string& name ) {mName=name;}
bool operator < ( const TestTeam& r) const
{
return GetPoints() < r.GetPoints();
}
private:
int16_t mPoints;
std::string mName;
};
int main(int argc, const char * argv[])
{
const uint32_t COUNT_LOWEST_ELEMENTS_TO_FIND = 3;
// Fill container by test data with a help of non-explicit constructor to solve your task
std::vector<TestTeam> VecTeam {3,5,8,9,11,2,14,7};
// Here you can do others manipulations with team data ...
//Sort vector by GetPoints overloaded in less operator. After sort first three elements will be with lowest points in container
std::sort(VecTeam.begin(), VecTeam.end());
//Print results as points - name
std::for_each( VecTeam.begin(), VecTeam.begin() + COUNT_LOWEST_ELEMENTS_TO_FIND, [] (TestTeam el)
{
std::cout << el.GetPoints() << " - " << el.GetName() << std::endl;
} );
}
I made test class TestTeam only to implement test logic for your object.
If you try launch the program you can get next results:
2 - Empty name
3 - Empty name
5 - Empty name
Program ended with exit code: 0

Change or delete elements of vector

I have following problem. My vector contains pairs of pairs (see example below).
In the example below I will push_back vector with some "random" data.
What will be best solution to delete the vector element if any of their values will be equal i.e. 100 and update value if less than 100.
i.e.
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
MyMap pair1;
MyMap pair2;
In first example I want to update this pair because pair1.first is less than 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 101;
pair2.second = 101;
In second example I want to delete this pair because pair2.first is equal to 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 100;
pair2.second = 101;
Using functor "check" I am able to delete one or more elements (in this example just one).
It is possible to increase every value of that pair by 1 using std::replace_if function?
Is there any function that will update this value if any of these values will be lower then "X" and delete if any of these values will be equal "X"?
I know how to do it writing my own function but I am curious.
#include "stdafx.h"
#include<algorithm>
#include<vector>
#include<iostream>
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
void PrintAll(std::vector<MyPair> & v);
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size);
class check
{
public:
check(int c)
: cmpValue(c)
{
}
bool operator()(const MyPair & mp) const
{
return (mp.first.first == cmpValue);
}
private:
int cmpValue;
};
int _tmain(int argc, _TCHAR* argv[])
{
const int size = 10;
std::vector<MyPair> vecotorOfMaps;
FillVectorWithSomeStuff(vecotorOfMaps, size);
PrintAll(vecotorOfMaps);
std::vector<MyPair>::iterator it = std::find_if(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
if (it != vecotorOfMaps.end()) vecotorOfMaps.erase(it);
PrintAll(vecotorOfMaps);
system("pause");
return 0;
}
std::ostream & operator<<(std::ostream & stream, const MyPair & mp)
{
stream << "First:First = " << mp.first.first << " First.Second = " << mp.first.second << std::endl;
stream << "Second:First = " << mp.second.first << " Second.Second = " << mp.second.second << std::endl;
stream << std::endl;
return stream;
}
void PrintAll(std::vector<MyPair> & v)
{
for (std::vector<MyPair>::iterator it = v.begin(); it != v.end(); ++it)
{
std::cout << *it;
}
}
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size)
{
for (int i = 0; i < size; ++i)
{
MyMap m1(i + i * 10, i + i * 20);
MyMap m2(i + i * 30, i + i * 40);
MyPair mp(m1, m2);
v.push_back(mp);
}
}
Use std::stable_partition, along with std::for_each:
#include <algorithm>
//...partition the elements in the vector
std::vector<MyPair>::iterator it =
std::stable_partition(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
//erase the ones equal to "check"
vecotorOfMaps.erase(vecotorOfMaps.begin(), it);
// adjust the ones that were left over
for_each(vecotorOfMaps.begin(), vecotorOfMaps.end(), add(1));
Basically, the stable_partition places all the items you will delete in the front of the array (the left side of the partiton it), and all of the other items to the right of it.
Then all that is done is to erase the items on the left of it (since they're equal to 100), and once that's done, go through the resulting vector, adding 1 to eac

How to use count() function in a vector<struct> according to specific criteria

I have a vector of type struct with some elements, and trying to count the number of occurrences of an element(value) in its corresponding column of the vector. I know how to count on a simple vector, e.g on vector of type string. But am stuck on vector<struct>. Any possible solution or suggestion?
Sample code:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
struct my_struct
{
std::string first_name;
std::string last_name;
};
int main()
{
std::vector<my_struct> my_vector(5);
my_vector[0].first_name = "David";
my_vector[0].last_name = "Andriw";
my_vector[1].first_name = "Jhon";
my_vector[1].last_name = "Monta";
my_vector[2].first_name = "Jams";
my_vector[2].last_name = "Ruth";
my_vector[3].first_name = "David";
my_vector[3].last_name = "AAA";
my_vector[4].first_name = "Jhon";
my_vector[4].last_name = "BBB";
for(int i = 0; i < my_vector.size(); i++)
{
int my_count=count(my_vector.begin(), my_vector.end(),my_vector[i].first_name);
/*I need help to count the number of occerencess of each "First_name" in a vector
For example: First_Name:- David COUNT:- 2 ...and so on for each first_names*/
std::cout << "First_Name: " << my_vector[i].first_name << "\tCOUNT: " << my_count << std::endl;
}
return 0;
}
but, the same code for a vector of type string,std::vector<std::string> works properly. see below:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> my_vector;
my_vector.push_back("David");
my_vector.push_back("Jhon");
my_vector.push_back("Jams");
my_vector.push_back("David");
my_vector.push_back("Jhon");
for(int i = 0; i < my_vector.size(); i++)
{
int my_count = count(my_vector.begin(), my_vector.end(),my_vector[i]); //this works good
std::cout << "First_Name: " << my_vector[i] << "\tCOUNT: " << my_count << std::endl;
}
return 0;
}
You have to use std::count_if with correct predicate:
int my_count = std::count_if(my_vector.begin(), my_vector.end(),
[&](const my_struct& s) {
return s.first_name == my_vector[i].first_name;
});
Demo
The functor to replace lambda in C++03:
struct CompareFirstName
{
explicit CompareFirstName(const std::string& s) : first_name(s) {}
bool operator () (const my_struct& person) const
{
return person.first_name == first_name;
}
std::string first_name;
};
and then
int my_count = std::count_if(my_vector.begin(), my_vector.end(),
CompareFirstName(my_vector[i].first_name));
Demo