How to use tolower with lambda function in map? C++ - c++

I want to change all the words stored in a map to lower case. with lambda function and transform, how should I do that?
std::map <string, int> M;
std::map<string, int> M1;
std::transform(M.begin(), M.end(), M1.begin(),
[](pair<const string, int> const & p) { ::tolower(p.first); });

You can't edit keys in a map, so you'll have to create a new map
Something along the lines of:
iterate over the values in your map
take a copy of the key
transform it to lowercase
insert it into your result map:
Example:
#include <iostream>
#include <map>
#include <algorithm>
int main()
{
std::map<std::string, int> map = {{ "HELLO", 1 }, { "WORLD", 2 }};
std::cout << "before:\n";
for (auto& kv : map)
{
std::cout << '\t' << kv.first << ":" << kv.second << '\n';
}
// create a new map with lowercase keys
std::map<std::string, int> out;
std::for_each(map.begin(), map.end(), [&](auto& kv)
{
std::string lower;
std::transform(kv.first.begin(), kv.first.end(), std::back_inserter(lower), tolower);
out[lower] = kv.second;
});
std::cout << "after:\n";
for (auto& kv : out)
{
std::cout << '\t' << kv.first << ":" << kv.second << '\n';
}
return 0;
}
output:
before:
HELLO:1
WORLD:2
after:
hello:1
world:2

std::transformassigns the result of the functor to elements in the destination range. This means that the destination iterator must be the start of range with same size as the input range. In your example, the destination is an empty map. The transform essentially is the following loop:
std::map <string, int> M;
std::map<string, int> M1;
for(auto i=M.begin(), j=M1.begin(); i != M.end(); ++i,++j)
{
*j = f(*i);
}
Decrementing j is illegal for empty containers, and doesn't really make sense for a map since you cannot change the key.
What you can see from this code is that your lambda is also incorrect. It should transform one (key value) pair into an object of the target type. In your case, the target type is the same.
You either have to resize the destination container before, e.g. by calling resize if it was a vector, or use an iterator which adapts the assignment to map::insert. The STL provides adaptors for this:
#include <map>
#include <string>
#include <cctype>
#include <iterator>
#include <algorithm>
int main() {
std::map <std::string, int> M;
std::map<std::string, int> M1;
std::transform(M.begin(), M.end(), std::inserter(M1, M1.begin()),
[](std::pair<const std::string, int> const & p)
{
std::string lowercase;
std::transform( p.first.begin(), p.first.end(),
std::back_inserter(lowercase),
[](auto c) {return std::tolower(c);} );
return std::make_pair(lowercase, p.second);
});
return 0;
}

If you want to use exactly std::transform then you can use the following approach
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include <cctype>
#include <iterator>
#include <utility>
int main ()
{
std::map<std::string, int> m1 = { { "FIRST", 1 }, { "SECOND", 2 } };
for ( const auto &p : m1 )
{
std::cout << p.first << '\t' << p.second << std::endl;
}
auto to_lower_case = []( const std::pair<const std::string, int> &p )
{
std::string t; t.reserve( p.first.size() );
std::transform( p.first.begin(), p.first.end(),
std::back_inserter( t ), ::tolower );
return std::make_pair( t, p.second );
};
std::cout << std::endl;
std::map<std::string, int> m2;
std::transform( m1.begin(), m1.end(),
std::inserter( m2, m2.begin() ), to_lower_case );
for ( const auto &p : m2 )
{
std::cout << p.first << '\t' << p.second << std::endl;
}
}
The program output is
FIRST 1
SECOND 2
first 1
second 2
In the program there are used std::transform two times.

Related

map,vector in c++ [duplicate]

This question already has answers here:
How do I print out the contents of a vector?
(31 answers)
Closed 2 years ago.
error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream’ and ‘std::pair<const std::__cxx11::basic_string, std::vector >’)
i want to same key and mutiple values, for example key is 10 values are 2,3,4
but "*iter" is wrong..
how to cout map,vector in c++?
In your code snippet the value of the expression *iter is an object of the type std::pair<std::string, std::vector<int>> for which the operator << is not defined.
And the error message
error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream’ and
‘std::pair<const std::__cxx11::basic_string, std::vector >’)
says about this.
The simplest way is to use the range-based for loop.
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <vector>
#include <map>
int main()
{
std::map<std::string, std::vector<int>> m;
m["10"].assign( { 2, 3, 4 } );
for ( const auto &p : m )
{
std::cout << p.first << ": ";
for ( const auto &item : p.second )
{
std::cout << item << ' ';
}
std::cout << '\n';
}
return 0;
}
The program output is
10: 2 3 4
If you want to write ordinary for-loops using iterators then the loops can look the following way.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <iterator>
int main()
{
std::map<std::string, std::vector<int>> m;
m["10"].assign( { 2, 3, 4 } );
for ( auto outer_it = std::begin( m ); outer_it != std::end( m ); ++outer_it )
{
std::cout << outer_it->first << ": ";
for ( auto inner_it = std::begin( outer_it->second );
inner_it != std::end( outer_it->second );
++inner_it )
{
std::cout << *inner_it << ' ';
}
std::cout << '\n';
}
return 0;
}
Again the program output is
10: 2 3 4
I suggest using structured bindings and range-based for loops:
std::map<std::string,std::vector<int>> m;
for (auto&[str, vec] : m) { // bind str to "first" in the pair and vec to "second"
std::cout << str << ':';
for(auto lineno : vec) std::cout << ' ' << lineno;
std::cout << '\n';
}
You can define how to print things via std::ostream like this:
#include <iostream>
#include <map>
#include <vector>
#include <string>
// define how to print std::pair<std::string, std::vector<int>>
std::ostream& operator<<(std::ostream& stream, const std::pair<std::string, std::vector<int>>& pair) {
stream << "(" << pair.first << ", {";
bool first = true;
for (int e : pair.second) {
if (!first) stream << ", ";
stream << e;
first = false;
}
stream << "})";
return stream;
}
int main(void) {
std::string yytext = "hoge";
int lineno = 42;
// below is copied from the question
std::map<std::string,std::vector<int>> m;
m[yytext].push_back(lineno);
std::map<std::string,std::vector<int>>::iterator iter;
for (iter=m.begin(); iter!=m.end(); iter++){
std::cout<<iter->first<<":"<<*iter<<std::endl;}
}

Printing a `vector<pair<int, int>>`

Here is my code:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
vector<pair<int, int>> v;
v.push_back({ 1, 5 });
v.push_back({ 2,3 });
v.push_back({ 1,2 });
sort(v.begin(), v.end());
copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
}
I'm getting an error C2679 binary "=": no operator found which takes a right-hand operand of type "std::pair<int, int>" (or there is no acceptable conversion). I have no idea what this means, or why copy doesn't print. Without copy, there is no error, but I want to print out vector v. Is there some other way to do htis?
In your code, you're trying to print std::pair<int, int> using a function printing one int, that's not going to work.
Since std::ostream doesn't have an overload taking std::pair by default, you have to provide an overload for the operator<< taking the type std::pair<int, int> yourself:
// This is necessary because ADL doesn't work here since
// the std::ostream_iterator will be looking for the overload
// in the std namespace.
struct IntPair : std::pair<int, int> {
using std::pair<int, int>::pair;
};
std::ostream& operator<<(std::ostream& o, const IntPair& p) {
o << p.first << " " << p.second;
return o;
}
int main() {
std::vector<IntPair> v;
v.push_back({ 1, 5 });
v.push_back({ 2, 3 });
v.push_back({ 1, 2 });
std::sort(v.begin(), v.end());
std::copy(v.begin(), v.end(), std::ostream_iterator<IntPair>(std::cout, " "));
}
After you sort you can use a for-each loop to iterate over the vector container and print the pairs:
for(const pair<int,int>& x: v)
{
cout << x.first << " " << x.second << "\n";
}
There's no standard way to cout a std::pair because there is no stream insertion (<<) operator overload for it. You could instead use std::for_each:
std::for_each(
v.begin(),
v.end(),
[](const auto& p) { std::cout << p.first << "," << p.second << std::endl; });
In my opinion this is the fastest way what I know
template<typename T, typename Y>
auto operator<<(std::ostream& os, const std::vector<std::pair<T,Y>>& pair) -> std::ostream&
{
for(const auto& el : pair)
os << el.first << ' ' << el.second << '\n';
return os;
}

How to insert vector of integers into Key, Value of std::map

Goal: Read numerical text files into vectors and then add the vectors to key,value std::map so that I can reference them by the key name I have specified for them, later.
Thought this would be easy and I am surprised that I can't find an answer for this already on StackOverflow.
Result Expected:
Print1 = {100,200,500,600}
Print2 = {7890,5678,34567,3,56}
Print3["NameA"] = Print1
Print3["NameB"] = Print2
If my process is inefficient or going in the wrong direction, I would appreciate the pointers.
I keep getting Semantic Issue build fails and no viable conversion from pair <const basic_string>
Current Code:
#include <string.h>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
int main()
{
std::vector<int> print1;
std::ifstream inputFile("numbers.txt");
// test file open
if (inputFile)
{
double value;
// read the elements in the file into a vector
while ( inputFile >> value ) {
print1.push_back(value);
}
}
inputFile.close();
std::vector<int> print2;
std::ifstream inputFile2("numbers2.txt");
// test file open
if (inputFile2)
{
double value;
// read the elements in the file into a vector
while ( inputFile2 >> value ) {
print2.push_back(value);
}
}
inputFile2.close();
std::map<std::string, std::vector<int>> contacts;
contacts["alice"] = print1;
contacts["bob"] = print2;
std::vector<std::string> keys(contacts.size());
std::vector<int> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
std::cout << "Keys:\n";
copy(keys.begin(), keys.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << "\n";
std::cout << "Values:\n";
copy(values.begin(), values.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
You can reference map element directly which will create an entry if doesn't exist, and just fill it from the file read loop:
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> m;
int num;
auto &&alice = m["alice"];
std::ifstream if_alice("numbers1.txt");
while (if_alice >> num)
alice.push_back(num);
if_alice.close();
auto &&bob = m["bob"];
std::ifstream if_bob("numbers2.txt");
while (if_bob >> num)
bob.push_back(num);
if_bob.close();
// test
for (auto &&data : m)
{
std::cout << "Name: " << data.first << "\t";
for (int num : data.second)
std::cout << num << " ";
std::cout << "\n";
}
}
First of all, there is no point in arguing that Xcode didn't show any error msgs for your code. Try either turning on all the compiler warnings or try in online compilers. The result will be not disappointing: https://godbolt.org/z/cU54GX
If I have understood correctly, you want to store your information from two files(the integer values) in a std::map, where its key = std::string and value = vector of integer array.
If so,
1. You have your problem starting from reading integers from files.
There you are using double for no reason and storing to
std::vector<int> (i,e print1 and print2).
2. Secondly, what if your file has not been open?. inputFile.close();
and inputFile2.close(); will close it anyway, without knowing the
fact. This is wrong.
The proper way would be:
inputFile.open("numbers.txt", std::ios::in); // opening mode
if (inputFile.is_open()) {
// do stuff
inputFile.close(); // you need closing only when file has been opened
}
3. If your intention is to only print keys and values, you don't
need to parse them to different vectors.
You can do it directly:
for(const std::pair<kType, vType>& mapEntry: contacts)
{
std::cout << "Key: " << mapEntry.first << " Values: ";
for(const int values: mapEntry.second) std::cout << values << " ";
std::cout << std::endl;
}
In c++17 you can use Structured binding
for(const auto& [Key, Values]: contacts)
{
std::cout << "Key: " << Key << " Values: ";
for(const int value: Values) std::cout << value << " ";
std::cout << std::endl;
}
4. If you really want to parse them to a different vector; first of all, the data structure for storing keys is wrong:
std::vector<int> values(contacts.size());
^^^^^^
which should have been a vector of vector of integers, as your vType = std::vector<int>. That is,
std::vector<std::vector<int>> values
^^^^^^^^^^^^^^^^^^
Secondly, you have the functions key(const std::pair<std::string, std::string>& keyValue) and value(const std::pair<std::string, std::string>& keyValue) wrong, where you are passing a pair of strings as keys and values.
This should have been
typedef std::string kType; // type of your map's key
typedef std::vector<int> vType;// value of your map's value
std::pair<kType, vType>
However, you can simply replace with lambdas, which would be more intuitive, in the sense of having the functions next to the line where you needed. For example,
std::vector<kType> keysVec;
keysVec.reserve(contacts.size());
auto getOnlyKeys = [](const std::pair<kType, vType>& mapEntry){ return mapEntry.first; };
std::transform(contacts.begin(), contacts.end(), std::back_inserter(keysVec), getOnlyKeys);
See an example code here

std::set<pair<int,int> > -- find only using pair<>::first as the key

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?

Insert more than one pair in c++

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