I have the program that has two vectors of names and ages. It sorts the names vector and keeps the age vector in the correct order to match the sorted name vector. Now, I want to make a function from existing code, but I have some issues.
Existing code:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iomanip>
using namespace std;
int main() {
vector<string> names {"One", "Two", "Three", "Four", "Five"};
vector<unsigned int> ages { 1, 2, 3, 4, 5};
const vector<string> namesCopy = names;
sort(begin(names), end(names));
decltype(ages) sortedAges(ages.size());
for(int i = 0; i < namesCopy.size(); ++i) {
const auto iter = lower_bound(begin(names), end(names), namesCopy[i]);
const auto pos = iter - begin(names);
sortedAges[pos] = ages[i];
}
for(int i = 0 ; i < names.size() ; ++i)
cout << setw(10) << names[i] << setw(4) << sortedAges[i] << '\n' ;
}
Output
Function:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iomanip>
using namespace std;
int test(vector<string> testNames, vector<string> testNamesCopy, vector<unsigned int> testAges, vector<unsigned int> testSortedAges) {
for(int i = 0; i < testNamesCopy.size(); ++i) {
const auto iter = lower_bound(begin(testNames), end(testNames), testNamesCopy[i]);
const auto pos = iter - begin(testNames);
return testSortedAges[pos] = testAges[i];
}
}
int main() {
vector<string> names {"One", "Two", "Three", "Four", "Five"};
vector<unsigned int> ages { 1, 2, 3, 4, 5};
const auto namesCopy = names;
sort(begin(names), end(names));
decltype(ages) sortedAges(ages.size());
for(int i = 0 ; i < names.size() ; ++i)
cout << setw(10) << names[i] << setw(4) << test(names, namesCopy, ages, sortedAges) << '\n' ;
}
Output 2
I think you are approaching this the wrong way. Having 2 vector that you sort but have to keep in the same order is error prone. Instead you should use a vector of pair.
std::vector<std::pair<std::string, int>> idendityVec;
Then you can sort by the name (the first element of the pair) by doing
std::sort(idendityVec.begin(), idendityVec.end());
If you want to sort by age, you can declare your own comparaison function and use it in the sort :
bool lesserAge(const pair<std::string,int> &a,
const pair<std::string,int> &b)
{
return (a.second < b.second);
}
std::sort(idendityVec.begin(), idendityVec.end(), lesserAge);
Which gives you something like this :
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
bool lesserAge(const std::pair<std::string, int> &a,
const std::pair<std::string, int> &b)
{
return (a.second < b.second);
}
int main()
{
std::vector<std::pair<std::string, int>> idendityVec = {std::make_pair("three", 3), std::make_pair("four", 4), std::make_pair("two", 2), std::make_pair("one", 1)};
for (auto v : idendityVec)
{
std::cout << "Name=" << v.first << ", age=" << v.second << std::endl;
}
// Sort by age i.e. second element
std::sort(idendityVec.begin(), idendityVec.end(), lesserAge);
for (auto v : idendityVec)
{
std::cout << "Name=" << v.first << ", age=" << v.second << std::endl;
}
//Sort by name i.e first element
std::sort(idendityVec.begin(), idendityVec.end());
for (auto v : idendityVec)
{
std::cout << "Name=" << v.first << ", age=" << v.second << std::endl;
}
}
vector<string> names {"One", "Two", "Three", "Four", "Five"};
vector<unsigned int> ages { 1, 2, 3, 4, 5};
names and ages seem connected in such a way that it'd be best to group them together in a class. We can use a simple struct which, by default, gives you direct access to its members, just like you have access to all the names and ages in your current solution. You can start with this:
struct person { // ... or animal, or thing. Give it a meaningful name.
std::string name{};
unsigned age{};
};
Now you can create a std::vector<person> instead of having two unconnected vectors, which makes sorting and general handling of the data a bit of a hassle.
With the above, sorting and printing etc. becomes more straight forward. I've used lambdas to create the sorting functions in the example:
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <tuple> // std::tie
#include <iomanip>
struct person {
std::string name{};
unsigned age{};
};
// print one "person"
std::ostream& operator<<(std::ostream& os, const person& p) {
return os << std::setw(10) << p.name << std::setw(4) << p.age;
}
int main() {
// one vector with all the persons
std::vector<person> persons{
{"One", 1},
{"Two", 2},
{"Three", 3},
{"Four", 4},
{"Five", 5}
};
// sort on name first, age second (if names are equal) - ascending order
std::sort(persons.begin(), persons.end(), [](const person& a, const person& b) {
return std::tie(a.name, a.age) < std::tie(b.name, b.age);
});
// print the current order:
for(const auto& p : persons) std::cout << p << "\n";
std::cout << "--\n";
// sort on age first, name second (if ages are equal) - ascending order
std::sort(persons.begin(), persons.end(), [](const person& a, const person& b) {
return std::tie(a.age, a.name) < std::tie(b.age, b.name);
});
// print the current order:
for(const auto& p : persons) std::cout << p << "\n";
}
Related
Imagine that I have an array of "person" objects:
#include <iostream>
class person{
public:
int age;
std::string name;
};
Is it possible to get an array of the same size with the age (or the name) of each person? I know how to do it with a loop but I hoped there was some kind of "map" that could get me the same in one line.
Here's how it can be done with std::transform, std::back_inserter, and a lambda:
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
struct person{
int age;
std::string name;
};
int main()
{
std::vector<person> pp = {{16, "Bob"},{32, "Alice"}};
std::vector<int> ages;
std::transform(
pp.begin(),
pp.end(),
std::back_inserter(ages),
[](const person& p) -> int { return p.age; });
for(auto v: ages)
std::cout << v << " ";
}
Output:
16 32
Live demo
UPDATE: to avoid unnecessary re-allocations you can call ages.reserve(pp.size()) before a call of std::transform
For the sake of generality I assume with array you mean a dynamically sized array, then the full example would be:
#include <string>
struct person {
int age;
std::string name;
};
std::vector<int> getAge(std::vector<person> p) {
std::vector<int> result;
result.reserve(p.size());
for (auto& e : p) result.push_back(e.age);
return result;
}
or you using std::transform that would be
#include <algorithm>
int main() {
std::vector<person> foo(10);
std::vector<int> ages(10);
std::transform(foo.begin(),foo.end(),ages.begin(),[](person& p){return p.age;});
}
In any case, there is no magic way to get an array of members from an array of instances. There must be a loop somewhere (transform just does a very good job at hiding that loop from you).
Yes, you could, using transform both for legacy arrays and for standard containers such as std::vector.
Using legacy arrays
person persons[2] = { { 20, "name1" }, { 21, "name2" } };
for (auto & person : persons)
cout << person.age << " " << person.name << endl;
decltype(person::age) ages[sizeof(persons)/sizeof(persons[0])];
std::transform(std::begin(persons), std::end(persons), std::begin(ages), [](person & p) -> decltype(person::age) { return p.age; });
for (auto & age : ages)
cout << age << endl;
Using std::vector
std::vector<person> persons = { { 20, "name1" }, { 21, "name2" } };
for (auto & person : persons)
cout << person.age << " " << person.name << endl;
std::vector<decltype(person::age)> ages(persons.size());
std::transform(persons.begin(), persons.end(), ages.begin(), [](person & p) -> decltype(person::age) { return p.age; });
for (auto & age : ages)
cout << age << endl;
This dynamically extracts the ages of all the persons in the original container.
I have a string like "aabcdba" now I want to store the position of different character's position. I am trying to store using vector and unordered_map. Is there any good approach to store the position of different characters?
void topKFrequent(string s) {
vector<vector<int> >v(123);
//unordered_map<char, vector<int>>m;
for(int i=0;i<s.size();i++) {
v[s[i]].push_back(i);
// m[s[i]].push_back(i);
}
for(int i=0;i<123;i++) {
for(int j=0;j<v[i].size();j++) {
char ch=i;
cout<<ch<<"->"<<v[i][j]<<endl;
}
}
}
if string = "aabcdba", I want the following result:
a->0,1,6;
b->2,5;
c->3;
d->4;
You could use a map<char, vector<unsigned int> >.
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
map<char, vector<unsigned int> > storePos(string s)
{
map<char, vector<unsigned int> > charPos;
for(int i=0;i<s.size();i++)
{
auto itr = charPos.find(s[i]);
if(itr != charPos.end())
{
itr->second.push_back(i);
}
else
{
charPos[s[i]] = vector<unsigned int>(1, i);
}
}
return charPos;
}
int main(void)
{
string example = "aabcdba";
auto result = storePos(example);
for(auto itr1 = result.begin(); itr1 != result.end(); itr1 ++)
{
cout << "Letter: " << itr1->first << ", Locations: ";
for(auto itr2 = itr1->second.begin(); itr2 != itr1->second.end();
itr2 ++)
{
cout << *itr2 << " ";
}
cout << endl;
}
}
If you really want to store ordinal positions in the original string sequence, you can do so with either an unordered or ordered map of char to vector, where char is the key, and the vector contains the positions. Using an unordered map will not give you the lexicographical ordering of keys you seem to be seeking, but will nonetheless give you accurate positional vectors.
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
int main()
{
std::string s = "aabcdba";
std::unordered_map<char, std::vector<unsigned int>> mymap;
for (unsigned i=0; i<s.size(); ++i)
mymap[s[i]].push_back(i);
for (auto const& pr : mymap)
{
std::cout << pr.first << "->";
auto it = pr.second.cbegin();
std::cout << *it;
while (++it != pr.second.cend())
std::cout << ',' << *it;
std::cout << ";\n";
}
}
Output
d->4;
c->3;
b->2,5;
a->0,1,6;
If you want lexicographical ordering, the simplest alternative is to simply using a regular ordered map instead. Changing only this:
std::unordered_map<char, std::vector<unsigned int>> mymap;
to this:
std::map<char, std::vector<unsigned int>> mymap;
and including the appropriate header delivers us this for output:
a->0,1,6;
b->2,5;
c->3;
d->4;
which fits exactly what you seem to be looking for.
A possible implementation to store the positions could be using unordered_multimap: (where the key characters can be repeated).
void storePos(string s) {
unordered_multimap<char, int>m;
for(int i=0;i<s.size();i++) {
m.insert(make_pair(s[i],i));
}
}
[EDITED]
But the output may depend on how you use it, or print out the data.
For example, consider the use of a std::multimap instead of std::unordered_map, to populate it you just do:
multimap<char, int>m;
void storePos(string s) {
for(int i=0;i<s.size();i++) {
m.insert(make_pair(s[i],i));
}
}
And to print the data you could have the following method:
void printPos()
{
std::multimap<char,int>::iterator it,itup;
for (it = m.begin(); it != m.end(); )
{
cout << (*it).first << " -> ";
itup = m.upper_bound ((*it).first );
// print range [it,itup):
for (it; it!=itup; ++it)
{
cout << (*it).second << ", ";
}
cout << endl;
}
}
Output:
a -> 0, 1, 6,
b -> 2, 5,
c -> 3,
d -> 4,
Try this!
I am trying to create a program to retain only unique elements in a vector
for example:
vector<string> i = "one", "one", "two".
the output would be:
vector<string> i = "one", "two"
**** This is an implementation of the answer as a function. I get the error vector iterator incompatible when I run it with a sample vector.
void simplifyVector(vector<string> i){
/*vector<string>*/;
sort(i.begin(), i.end());
auto iter = unique(i.begin(), i.end());
while (iter != i.end())
{
i.erase(iter);
}
for (const auto &s : i)
{
cout << s << " ";
}
cout << endl;
}
Use sort and unique. Use sort to but all duplicate values adjacent to each other than unique puts all vales that are adjacent to each other at the end and returns an iterator pointing to the first duplicate element. Then we use a while loop to remove those elements. Remember generic algorithms never delete elements in a container.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
std::vector<std::string> i = {"one", "one", "two"};
std::sort(i.begin(), i.end());
auto iter = std::unique(i.begin(), i.end());
erase(iter, i.end());
for (const auto &s : i)
{
std::cout << s << " ";
}
std::cout << std::endl;
return 0;
}
So I'm having a Red Black Tree containing pairs of int, and when i call .find(x) function it will search for x (both first and second), but i want to make it ignore the second value, and look only for the first value. How can i do that?
In general, it is not possible. But for your limited case of pair of ints you can fake it using upper_bound() and std::numeric_limits<int>::min():
#include <iostream>
#include <iomanip>
#include <limits>
#include <set>
int main()
{
using key_type = std::pair<int, int>;
std::set<key_type> s { {1, -1}, {1, 3}, {2, 10}, {3, 42} };
auto it = s.upper_bound (key_type (2, std::numeric_limits<int>::min ()));
std::cout << "(" << it->first << "; " << it->second << ")\n";
}
Live on Coliru
auto fn = [](const pair<int, int>&a, const pair <int, int>&b) {
return a.first < b.first;
};
set<pair<int, int>, decltype(fn)> my_set(fn);
my_set is now a set that uses only pair.first as the key
Example:
#include <iostream>
#include <string>
#include <set>
using namespace std;
int main()
{
auto fn = [](const pair<int, int>&a, const pair <int, int>&b) {
return a.first < b.first;
};
set<pair<int, int>, decltype(fn)> my_set(fn);
my_set.insert({1, 123});
my_set.insert({4, 456});
my_set.insert({7, 789});
auto iter = my_set.find({4, 0});
if (iter != my_set.end()) {
cout << "first: " << iter->first << ", second: " << iter->second << "\n";
} else {
cout << "not found\n";
}
return 0;
}
prints
first: 4, second: 456
change my_set to just set<pair<int, int>> my_set; and it will print not found
Of course, keying only on first is arguably a map<int, int> so why not just do that?
I have a map like this:
map<string, map<int, int>> collector;
And I have no idea how to insert data in my map. If I had
map<string, int> collector;
with only key-value I would use
collector.insert(pair<string, int>)(name,money));
But what is the way of inserting when we have map in map. I tried to do:
typedef map<int, int> map_;
for(iteration = collector.begin(); iteration != collector.end(); iteration++) {
iteration = collector.find(word);
if(iteration == collector.end()) {
iteration = collector.insert(map_::value_type(num,num).first;
}
}
This way is not working for me.
Here are some ways to insert into your data structure:
#include <iostream> // cout
#include <map>
#include <string>
#include <utility> // make_pair
using namespace std;
int main()
{
using Collector = map<string, map<int, int>>;
Collector collector;
collector["USD"] = Collector::mapped_type{ { 1, 3 }, { 0, 8 } };
collector["EUR"].insert(make_pair(4, 5));
collector["EUR"].insert(make_pair(6, 7));
collector["CHF"][2] = 4;
const Collector::mapped_type jpyIntegers { { 10, 20 }, { 100, 200 } };
collector.insert(make_pair("JPY", jpyIntegers));
collector["MMK"];
for (const auto& a: collector) {
cout << a.first << ": ";
for (const auto& i: a.second) {
cout << "(" << i.first << "|" << i.second << ")";
}
cout << "\n";
}
}