Simplify a vector to have only unique elements - c++

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;
}

Related

Problems with making vector function from the existing code C++

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";
}

How to check if elements of std::vector<std::string> start with certain sub-string?

I have a very large std::vector v of type std::vector<std::string> v. Now I want to compare which of the elements in the vector start with a certain substring str. What is the fastest possible way to do that?
I was thinking of a for-loop that iteratively compares the start of each element of v with the substring str. I first tried
std::string substring = "bla";
for (long unsigned int i = 0; i < v.size(); i++)
{
if (!strncmp(v[i].c_str(), substring.c_str(), substring.size()))
{
std::cout << "Item found: " << v[i] << std::endl;
}
}
Which is c mixed with c++ and I am not happy with that.
What better alternatives are there?
You can write completely a c++ code.
If you want to find all the elements satisfying the condition, you can not avoid iterating through the entire vector.
But you could use better range-based for-loop instead of index based loop to iterate through the vector, and check wether str.find(substring) == 0(credits #PiotrSkotnicki).
Here is the example code:
(See online)
#include <iostream>
#include <string>
#include <vector>
int main()
{
const std::string substring{ "bla" };
std::vector<std::string> vecString{ {"bllll"}, {"bllll"}, {"blasomething"} };
// iterate through the vector by range based for-loop
// here `auto` deduded to `std::string` as you have vector of strings(i.e. `vecString`)
for (const auto& str : vecString)
{
if (str.find(substring) == 0) {
std::cout << str << " is a match\n";
// do something more with str
}
}
return 0;
}
Alternatively using std::for_each, along with a lambda function you could write the following. Read more about the lambdas here: What is a lambda expression in C++11?
(See online)
#include <algorithm> // std::for_each
std::for_each(std::cbegin(vecString), std::cend(vecString), [&substring](const auto& str)
{
if (str.find(substring) == 0)
{
std::cout << str << " is a match\n";
// do something more with str
}
});
If you are interested only the first match in the vector of string s, making use of standard algorithm std::find_if as follows
#include <algorithm> // std::find_if
const auto iter = std::find_if(std::cbegin(vecString), std::cend(vecString),
[&substring](const auto& str) {
return str.find(substring) == 0;
}
);
if (iter != std::cend(vecString))
{
// do something
}
If you have an unsorted container you can't get better than O(n) in time complexity, which means iterating over the whole container in a linear manner (i.e. for loop). If your container was sorted (ex. std::set instead of std::vector) you would get O(log n) which is a lot better (binary search).
Prior to C++17, I can't come up with a better solution than yours (since creating a substring via std::string::substr means copying the substring unnecessarily). However C++17 introduced std::string_view which doesn't do any copying. There should be no noticable performance difference with compiler optimizations enabled.
std::vector<std::string> v { "abcd", "abcdefg", "aaaabbbb", "abc", "ab"};
std::string_view query = "abc";
for (auto const& str : v)
{
if (str.size() < query.size())
continue;
auto probe = std::string_view(str).substr(0, query.size());
if (query == probe)
std::cout << "Item found: " << str << "\n";
}
Live example
And here is the std::set version for the quicker search:
std::set<std::string> v { "abcd", "abcdefg", "aaaabbbb", "abc", "ab"};
std::string query = "abc";
for (auto it = v.lower_bound(query); it != v.end(); ++it)
{
auto probe = std::string_view(*it).substr(0, query.size());
if (query == probe)
std::cout << "Item found: " << *it << "\n";
else
break;
}
Live example
You can using c++20 std::string_view::start_with:
std::vector<std::string> v = {...};
std::string_view prefix = "bla";
for (std::string_view sv : v)
if (sv.starts_with(prefix))
std::cout << "Item found: " << sv << std::endl;

I want to reverse the values of map and print it using range based for loop.

I have done the programming but it is not reversing. I have used a different map to put the values in reverse order,but it still shows the same. My main question was to traverse backward and print the values using range based loop.
#include "stdafx.h"
#include <iostream>
#include<conio.h>
#include <stdio.h>
#include<vector>
#include<map>
#include<utility>
#include<set>
map<int, int>m1;
for (int i = 1; i <= 100; ++i)
{
m1.insert({ i,i });
}
for (const auto &y :m1)
{
cout <<"("<< y.first << " "<<y.second << ")" <<" " ;
}
cout << endl << endl;
map<int, int>m2;
map<int, int>::reverse_iterator iter;
for (auto iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
m2.insert({ iter->first,iter->second });
}
for (const auto &y : m2)
{
cout << "(" << y.first << " " << y.second << ")" << " ";
}
As Some Programmer Dude pointed out, but for the completeness of my answer, a std::map is sorted on the key, no matter what order you insert the elements. One option would be to create a new map with the opposite sorting, but that doesn't seem to be what you really want.
It seems you know how about reverse iterators, but not how to get at them when using range-based for. Since it operates on a range, i.e. some type that provides begin and end iterators, you need to create some wrapper around your map that provides this.
Here's a general one I just put together than works in C++11. It won't cover every possible case, and can be made a bit neater in C++14, but it will work for you.
#include <iostream>
#include <iterator>
// The wrapper type that does reversal
template <typename Range>
class Reverser {
Range& r_;
public:
using iterator_type = std::reverse_iterator<decltype(std::begin(r_))>;
Reverser(Range& r) : r_(r) {}
iterator_type begin() { return iterator_type(std::end(r_)); }
iterator_type end() { return iterator_type(std::begin(r_)); }
};
// Helper creation function
template <typename Range>
Reverser<Range> reverse(Range& r)
{
return Reverser<Range>(r);
}
int main()
{
int vals[] = {1, 2, 3, 4, 5};
for (auto i : reverse(vals))
std::cout << i << '\n';
}
This outputs:
$ ./reverse
5
4
3
2
1
(You may also find libraries that provide a similar adapter; Eric Niebler is working on a ranges library for The Standard.)
Also, please reconsider your use of what are often considered bad practices: using namespace std; and endl (those are links to explanations).
Here's an example of iterating backward through a std::map:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, int> m;
m[1] = 1;
m[2] = 2;
m[3] = 3;
for (auto iter = m.rbegin(); iter != m.rend(); ++iter) {
std::cout << iter->first << ": " << iter->second << std::endl;
}
}
If you are pre-C++11, you'll just need to spell out auto, which is:
std::map<int, int>::reverse_iterator
If you're using boost, you can use a range-based for loop with a reverse adapter:
#include <boost/range/adaptor/reversed.hpp>
for (auto& iter : boost::adaptors::reverse(m)) {
std::cout << iter.first << ": " << iter.second << std::endl;
}
If you only need to print the elements in the map in reverse order,you don't need another map for it,you can do this:
std::map<int, int>::reverse_iterator iter;
for (iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
std::cout << "(" << iter->first << " " << iter->second << ")" << " ";
}

How to store different character's positon using vector or map

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!

C++ Vector to CSV by adding Comma after each element

vector<string> v;
v.push_back("A");
v.push_back("B");
v.push_back("C");
v.push_back("D");
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) {
//printout
cout << *it << endl;
}
I like to add a comma after each element as follow:
A,B,C,D
I tried researching on Google, but I only found CSV to vector.
Loop way:
for (vector<string>::iterator it = v.begin(); it != v.end(); ++it) {
if (it != v.begin()) cout << ',';
cout << *it;
}
"Clever" way:
#include <algorithm>
#include <iterator>
if (v.size() >= 2)
copy(v.begin(), v.end()-1, ostream_iterator<string>(cout, ","));
if (v.size() >= 1)
cout << v.back();
With a normal ostream_iterator, you'll get a comma after every data item -- including the last, where you don't want one.
I posted an infix_iterator in a previous answer that fixes this problem, only putting commas between the data items, not after the final one.
Below should do the job. Thanks.
ofstream CSVToFile("ava.csv", ios::out | ios::binary);
//////////////////vector element to CSV////////////////////////////////////////
for (std::vector<string>::iterator i = ParamHeaders.begin(); i != ParamHeaders.end(); i++)
{
if (i != ParamHeaders.begin())
{
CSVToFile << ",";
std::cout << ",";
}
std::cout << *i;
CSVToFile << *i;
}
This is an old question but I noticed that a couple of people used two explicit tests to stop the last comma from appearing. That's unnecessary. Only need to test if the vector is not empty. If the vector contains one element then the copy becomes a no-op.
std::vector<std::string> V { "A", "B", "C", "D" };
if (!V.empty()) {
std::copy(V.begin(), V.end() - 1, std::ostream_iterator<std::string>(std::cout, ", "));
std::cout << V.back();
}
If you want a function that takes begin and end args:
template<typename OS, typename IT>
OS& csv(OS& os, IT begin, IT end, char const* delim = ", ") {
if (begin != end) {
using value_type = typename std::iterator_traits<IT>::value_type;
std::copy(begin, end - 1, std::ostream_iterator<value_type>(os, delim));
os << end[-1];
}
return os;
}
Does really nobody know the std::experimental::ostream_joiner?. See here. To be used in algorithms, like std::copy
Or, if you do not want to use std::experimental alternatively, std::exchange? See here
Like this:
#include <iostream>
#include <vector>
#include <utility>
int main() {
std::vector data{1,2,3,4};
bool showComma{};
for (const int i: data) std::cout << (std::exchange(showComma, true) ? "," : "") << i;
}
Maybe, I have a misunderstanding . . .
To avoid the trailing comma, loop until v.end() - 1, and output v.back() for instance:
#include <vector>
#include <iostream>
#include <iterator>
#include <string>
#include <iostream>
template <class Val>
void Out(const std::vector<Val>& v)
{
if (v.size() > 1)
std::copy(v.begin(), v.end() - 1, std::ostream_iterator<Val>(std::cout, ", "));
if (v.size())
std::cout << v.back() << std::endl;
}
int main()
{
const char* strings[] = {"A", "B", "C", "D"};
Out(std::vector<std::string>(strings, strings + sizeof(strings) / sizeof(const char*)));
const int ints[] = {1, 2, 3, 4};
Out(std::vector<int>(ints, ints + sizeof(ints) / sizeof(int)));
}
BTW you posted:
vector<string> v;
//...
for (vector<int>::iterator it = v.begin(); //...
which is unlikely to compile :)