Deleting empty elements from vector - c++

I am trying to delete empty entries from std::vector. Here is a sample code, but something is wrong here.
#include <iostream>
#include <string>
#include<vector>
#include <cctype>
int main()
{
std::vector<std::string> s1 = {"a"," ", "", "b","c"," ","d"};
for (auto it = s1.begin(); it != s1.end() && isspace(*it); )
{
it = s1.erase(it);
}
std::cout<<"vector size = "<<s1.size();
for (auto &i:s1)
std::cout<<i<<"\n";
}
I am running a for loop to find out empty elements and deleting from there. There should be STL method too, but not sure how it will work.

It seems you mean the following
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<std::string> v = { "a", " ", "", "b", "c", " ", "d" };
auto is_empty = []( const std::string &s )
{
return s.find_first_not_of( " \t" ) == std::string::npos;
};
v.erase( std::remove_if( std::begin( v ), std::end( v ), is_empty ), std::end( v ) );
for ( const auto &s : v )
{
std::cout << "\"" << s << "\" ";
}
std::cout << std::endl;
return 0;
}
The program output is
"a" "b" "c" "d"
As for your code then it is inefficient because you are trying to remove each found element separately and this loop for example
for (auto it = s1.begin(); it != s1.end() && isspace(*it); )
{
it = s1.erase(it);
}
can iterate never because the first element is not satisfies the condition isspace(*it) that moreover is invalid. That is you are supplying an object of the type std::string to a function that expects an object of the type char (more precisely of the type int).
If to use the C function isspace then the program can look the following way.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <cctype>
int main()
{
std::vector<std::string> v = { "a", " ", "", "b", "c", " ", "d" };
auto is_empty = []( const std::string &s )
{
return std::all_of( std::begin( s ), std::end( s ),
[]( char c )
{
return std::isspace( ( unsigned char )c );
} );
};
v.erase( std::remove_if( std::begin( v ), std::end( v ), is_empty ), std::end( v ) );
for ( const auto &s : v )
{
std::cout << "\"" << s << "\" ";
}
std::cout << std::endl;
return 0;
}
The program output is the same as shown above.

Related

copy 2d vector without first row and column

Just like in topic. I would like to copy one vector to another without first row and column.
'''
std::vector<std::vector<int>> v2(v1.size()-1,std::vector<int>(v1.size()-1));
std::copy((v1.begin()+1)->begin()+1,v1.end()->end(),v2.begin()->begin());
return v2;
'''
Using C++ and views it is easy to drop items while enumerating.
So you can avoid using raw or iterator loops.
Live demo here : https://godbolt.org/z/8xz91Y8cK
#include <ranges>
#include <iostream>
#include <vector>
auto reduce_copy(const std::vector<std::vector<int>> values)
{
std::vector<std::vector<int>> retval{};
// drop first row
for (const auto& row : values | std::views::drop(1))
{
// add a new row to retval
auto& new_row = retval.emplace_back();
// drop first column
for (const auto& col : row | std::views::drop(1))
{
new_row.emplace_back(col);
}
}
return retval;
}
int main()
{
std::vector<std::vector<int>> values{ {1,2,3}, {4,5,6}, {7,8,9} };
auto result = reduce_copy(values);
for (const auto& row : result)
{
for (const auto& value : row)
{
std::cout << value << " ";
}
std::cout << "\n";
}
return 0;
}
You can use for example the ordinary for loop
#include <vector>
#include <iterator>
#include <algorithm>
//...
for ( auto first = std::next( std::begin( v1 ) ), target = std::begin( v2 );
first != std::end( v1 );
++first, ++target )
{
std::copy( std::next( std::begin( *first ) ), std::end( *first ), std::begin( *target ) );
}

Counting the appearance of words in a vector and listing those in a list, C++

I have a cpp vector containing separate words and I need to count how many times a word appears using a list. I try to iterate through the list but failing with the comparison of the two STL containers, whether the following word is already in my list or not. If not, I want to add that word to my list with an appearance of 1. I have a struct that counts the times a word appeared in the text.
The following code returns a list of words and numbers, but not each in my vector and I can't see why.
struct counter{
string word;
int sum = 1;
counter(){};
counter(string word): word(word){};
};
list<counter> list_count(vector<string> &text){
list<counter> word_count;
list<counter>::iterator it = word_count.begin();
for(string t:text){
if(it != word_count.end()){
it -> sum++;
} else {
word_count.push_back(counter(t));
}
++it;
}
return word_count;
}
Thank you in advance.
list<counter> list_count(const vector<string>& text) {
list<counter> word_count;
for (const string& t : text) {
auto it = std::find_if(word_count.begin(), word_count.end(),
[&](const counter& c){ return c.word == t; });
if (it != word_count.end()) {
it -> sum++;
} else {
word_count.push_back(counter(t));
}
}
return word_count;
}
Untested code.
You are not actually searching the std::list at all. On every loop iteration through the std::vector, you need to search the entire std::list from front to back, eg:
#include <string>
#include <list>
#include <vector>
#include <algorithm>
using namespace std;
struct counter {
string word;
int sum = 1;
counter(const string &word): word(word) {}
};
list<counter> list_count(const vector<string> &text) {
list<counter> word_count;
for(const string &t: text) {
// perform an actual search here!
list<counter>::iterator it = find_if(
word_count.begin(), word_count.end(),
[&](counter &c){ return (c.word == t); }
);
if (it != word_count.end()) {
it->sum++;
} else {
word_count.emplace_back(t);
}
}
return word_count;
}
Live Demo
That being said, a std::list is a poor solution for counting elements. A better solution is to use a std::(unordered_)map instead (unless you need to preserve the order of the words found, which neither one will do), eg:
#include <string>
#include <map>
#include <vector>
using namespace std;
map<string, int> list_count(const vector<string> &text) {
map<string, int> word_count;
for(const string &t: text) {
word_count[t]++;
}
return word_count;
}
Live Demo (using std::map)
Live Demo (using std::unordered_map)
You are trying to use an inefficient approach. The standard class template list does not have random access to its elements. Each new element is appended to the end of the list. To find whether an element is already present in the list elements of it are traversed sequentially.
It would be much efficiently to use the standard container std::map . Moreover in this container words will be ordered.
For example you could declare
std::map<std::string, size_t> counters;
Nevertheless if you want to use the list then the function can look as it is shown in the demonstrative program below.
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &c ) { return c.word == s; } );
if ( it == std::end( word_count ) )
{
word_count.push_back( s );
}
else
{
++it->n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
first: 2
second: 1
Pay attention to that the definition of the struct counter is redundant. You could use instead the standard class std::pair. Here you are.
#include <iostream>
#include <string>
#include <utility>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
std::list<std::pair<std::string, size_t>> list_count( const std::vector<std::string> &text )
{
std::list<std::pair<std::string, size_t>> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &p ) { return p.first == s; } );
if ( it == std::end( word_count ) )
{
word_count.emplace_back( s, 1 );
}
else
{
++it->second;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
If to use std::map then the function will look very simple.
#include <iostream>
#include <string>
#include <vector>
#include <map>
std::map<std::string, size_t> list_count( const std::vector<std::string> &text )
{
std::map<std::string, size_t> word_count;
for ( const auto &s : text )
{
++word_count[s];
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
Using of the list will be efficient only in the case when the vector of strings is sorted.
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <list>
#include <vector>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
if ( word_count.empty() || word_count.back().word != s )
{
word_count.push_back( s );
}
else
{
++word_count.back().n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "A", "B", "B", "C", "C", "C", "D", "D", "E" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
A: 1
B: 2
C: 3
D: 2
E: 1

Replace a loop to count the number elements matching a criteria

Could we replace a loop with a strcmp by something easier to read, something similar to the C# extension methods?
I maintain legacy C++ and wonder how to start a transition to a more modern C++ and there is profusion of code that looks like this one:
int numberOfPipe = 10;
char* collection[5] = { "pompe","pipe","turbine","pompe", "pipe" };
// Count the pipes in the collection
int nPipeFound = 0;
int nPipe = 5;
for (int idx = 0; idx < nPipe; idx++)
{
if (strcmp(collection[idx], "pipe") == 0)
nPipeFound++;
}
cout << nPipeFound << endl;
Use the standard library:
Use std::count and use the std::string comparison.
#include <algorithm>
#include <iostream>
#include <string>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::count( std::begin( collection ), std::end( collection ), std::string{"pipe"});
std::cout << n_pipe_found << '\n';
}
Use std::count_if and write a predicate which does C string comparison for you.
#include <algorithm>
#include <cstring>
#include <iostream>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::count_if( std::begin( collection ), std::end( collection ),
[](char const * a) { return std::strcmp(a,"pipe") == 0; } );
std::cout << n_pipe_found << '\n';
}
You could also use a predicate like [](std::string const& a) { return a == "pipe"; } and again make use of std::string comparison.
Use std::accumulate if you need more fine grained control over counting.
#include <numeric>
#include <iostream>
#include <string>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::accumulate( std::begin( collection ), std::end( collection ), int{0},
[](int a, std::string const& b) { return a + (b == "pipe"); });
std::cout << n_pipe_found << '\n';
}

Take user input and search through a 2D array

I'm trying to make a program that takes in a string from the user, searches through a 2D array and if it matches a string in the array, print the entire row. So basically, if the user types in the name Bobby G, I'd like it to output Bobby G: ugly and stupid, if input is Billy it outputs Billy: bad, so on and so forth. Below is what I have so far. A bit of an explanation would be greatly appreciated.
#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main() {
//!! It only works with letters of the alphabet, so if I replaced Bobby G with the
//letter "A" It would output "ugly, and stupid", and then replace "Billy Smalls" with
//the letter "B" I'd get "Bad" so on and so forth, but I need it to work with the exact
// string, so user input of "Bobby G" outputs "ugly, and stupid"
std::string name[9][2] = {
{"Bobby G","ugly, and stupid"},
{"Billy","bad"},
{"John","smart and cool"},
{"Adam","amzing and beautiful"},
{"Bill","perfect"},
{"Turner","funny"},
{"Sonny","nice"},
{"Jack","radical"},
{"Frank","nice"}};
typedef std::string Full[2];
Full* last_Full = name + sizeof(name) / sizeof(Full);
struct Less {
bool operator () (const Full& a, const string& b) const
{
return a[0] < b;
}
};
std::string input;
std::cin >> input;
Less less_full;
Full* full = std::lower_bound(name, last_Full, input, less_full);
if(full == last_Full || (*full)[0] != input)
std::cout << "Not found" << std::endl;
else std::cout << (*full)[0] << ": " << (*full)[1] << std::endl;
system("Pause");
return 0;
}
I'd want to do it without nesting if statements and making it a mess.
I'm really having a hard time understanding what you wrote in your code but according to the description this should go something like (demo):
#include <iostream>
#include <unordered_map>
#include <string>
int main(void) {
std::unordered_map<std::string, std::string> name = {
{"Bobby G","ugly, and stupid"},
{"Billy","bad"},
{"John","smart and cool"},
{"Adam","amzing and beautiful"},
{"Bill","perfect"},
{"Turner","funny"},
{"Sonny","nice"},
{"Jack","radical"},
{"Frank","nice"}};
std::string in;
std::getline(std::cin,in);
if(name.count(in)){
std::cout << in << " " << name[in] << std::endl;
}
return 0;
}
And to save us headache in the future indent your code and don't make it look like ASCII art...
So basically what we use is an unordered_map which holds the names as keys and the sentences as values.
Then we use cin to receive input from the user and place it into string in.
The last step is to check whether we have such a string as a key in the map using count which will return 1 iff it contains this key.
But seriously, you have to do your reading more seriously; find a tutorial and/or a book and get your concepts straight.
The task is not so simple as it seems. So I up-voted your question.
First of all it is better to use a one-dimensional array of type std::pair<std::string, std::string> instead of the two-dimensional array that you use.
Secondly that to apply algorithm std::lower_bound the array must be sorted.
The code could look the following way
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <cctype>
int main()
{
std::pair<std::string, std::string> name[] =
{
{ "Bobby G", "ugly, and stupid" },
{ "Billy", "bad" },
{ "John", "smart and cool" },
{ "Adam", "amzing and beautiful" },
{ "Bill", "perfect" },
{ "Turner", "funny" },
{ "Sonny", "nice" },
{ "Jack", "radical" },
{ "Frank", "nice" }
};
std::sort( std::begin( name ), std::end( name ) );
auto compare_by_name =
[]( const std::pair<std::string, std::string> &p1,
const std::pair<std::string, std::string> &p2 )
{
return std::lexicographical_compare(
p1.first.begin(), p1.first.end(),
p2.first.begin(), p2.first.end(),
[]( char c1, char c2 )
{ return std::toupper( c1 ) < std::toupper( c2 ); } );
};
auto p = std::make_pair( std::string( "bobby g" ), std::string( "" ) );
auto it = std::equal_range( std::begin( name ), std::end( name ), p,
compare_by_name );
if ( it.first != it.second )
{
std::cout << it.first->first + ' ' + it.first->second << std::endl;
}
return 0;
}
The output is
Bobby G ugly, and stupid
it is about those participants who down voted your question.:)
If you compiler issues an error relative to array initializers you should substitute them the following way. For example
std::pair<std::string, std::string>( "Bobby G", "ugly, and stupid" ),
Here is code that uses other kind of initializers
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <cctype>
int main()
{
std::pair<std::string, std::string> name[] =
{
std::pair<std::string, std::string>( "Bobby G", "ugly, and stupid" ),
std::pair<std::string, std::string>( "Billy", "bad" ),
std::pair<std::string, std::string>( "John", "smart and cool" ),
std::pair<std::string, std::string>( "Adam", "amzing and beautiful" ),
std::pair<std::string, std::string>( "Bill", "perfect" ),
std::pair<std::string, std::string>( "Turner", "funny" ),
std::pair<std::string, std::string>( "Sonny", "nice" ),
std::pair<std::string, std::string>( "Jack", "radical" ),
std::pair<std::string, std::string>( "Frank", "nice" )
};
std::sort( std::begin( name ), std::end( name ) );
auto compare_by_name =
[]( const std::pair<std::string, std::string> &p1,
const std::pair<std::string, std::string> &p2 )
{
return std::lexicographical_compare(
p1.first.begin(), p1.first.end(),
p2.first.begin(), p2.first.end(),
[]( char c1, char c2 )
{ return std::toupper( c1 ) < std::toupper( c2 ); } );
};
auto p = std::make_pair( std::string( "bobby g" ), std::string( "" ) );
auto it = std::equal_range( std::begin( name ), std::end( name ), p,
compare_by_name );
if ( it.first != it.second )
{
std::cout << it.first->first + ' ' + it.first->second << std::endl;
}
return 0;
}
Instead of using long type name std::pair<std::string, std::string> you can introduce some typedef name as for example
typedef std::pair<std::string, std::string> Pair;
Here is the program that asks the user to enter a name
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <cctype>
int main()
{
std::pair<std::string, std::string> name[] =
{
std::pair<std::string, std::string>( "Bobby G", "ugly, and stupid" ),
std::pair<std::string, std::string>( "Billy", "bad" ),
std::pair<std::string, std::string>( "John", "smart and cool" ),
std::pair<std::string, std::string>( "Adam", "amzing and beautiful" ),
std::pair<std::string, std::string>( "Bill", "perfect" ),
std::pair<std::string, std::string>( "Turner", "funny" ),
std::pair<std::string, std::string>( "Sonny", "nice" ),
std::pair<std::string, std::string>( "Jack", "radical" ),
std::pair<std::string, std::string>( "Frank", "nice" )
};
std::sort( std::begin( name ), std::end( name ) );
auto compare_by_name =
[]( const std::pair<std::string, std::string> &p1,
const std::pair<std::string, std::string> &p2 )
{
return std::lexicographical_compare(
p1.first.begin(), p1.first.end(),
p2.first.begin(), p2.first.end(),
[]( char c1, char c2 )
{ return std::toupper( c1 ) < std::toupper( c2 ); } );
};
while ( true )
{
std::cout << "Enter name: ";
std:: string s;
std::getline( std::cin, s );
if ( s.empty() ) break;
auto p = std::make_pair( s, std::string( "" ) );
auto it = std::equal_range( std::begin( name ), std::end( name ), p,
compare_by_name );
if ( it.first != it.second )
{
std::cout << it.first->first + ' ' + it.first->second << std::endl;
}
else
{
std::cout << "The name is not found" << std::endl;
}
}
return 0;
}
Good luck.

How to create a new vector with particular columns from existing 2D vector in c++

I have 2D Vector (vector<vector<string>>) with a lot of columns (m*n) (Here I mentioned this 2D Vector as Maintable). I want to create a new vector with a few particular columns from main table.
For Example, Suppose If I have a main table with 12 columns, I want to take any 3 Non Contiguous columns from the main table into new 2D Vector. How to do that?
You can use something as the following
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
//...
const size_t N = 10;
std::string a[] = { "A", "B", "C", "D", "E", "F" };
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
std::vector<std::vector<std::string>> v2;
v2.reserve( v1.size() );
for ( const std::vector<std::string> &v : v1 )
{
v2.push_back( std::vector<std::string>(std::next( v.begin(), 2 ), std::next( v.begin(), 5 ) ) );
}
for ( const std::vector<std::string> &v : v2 )
{
for ( const std::string &s : v ) std::cout << s << ' ';
std::cout << std::endl;
}
It is simple to rewrite the code using the C++ 2003 syntax. For example you can write
std::vector<std::vector<std::string>> v1( N,
std::vector<std::string>( a, a + sizeof( a ) / sizeof( *a ) ) );
instead of
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
and so on.
EDIT: If the columns are not adjacent then you can use the following approach
#include <iostream>
#include <vector>
#include <array>
#include <string>
#include <iterator>
#include <algorithm>
int main()
{
const size_t N = 10;
const size_t M = 3;
std::string a[N] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
std::vector<std::vector<std::string>> v2;
v2.reserve( v1.size() );
std::array<std::vector<std::string>::size_type, M> indices = { 2, 5, 6 };
for ( const std::vector<std::string> &v : v1 )
{
std::vector<std::string> tmp( M );
std::transform( indices.begin(), indices.end(), tmp.begin(),
[&]( std::vector<std::string>::size_type i ) { return ( v[i] ); } );
v2.push_back( tmp );
}
for ( const std::vector<std::string> &v : v2 )
{
for ( const std::string &s : v ) std::cout << s << ' ';
std::cout << std::endl;
}
}